mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-12 01:07:35 +02:00
Mkdir was masking in.Mode with wfs.option.Umask on top of the kernel's VFS umask pass, so a caller with umask=0 who requested mkdir(0777) got 0755 (0777 & ~022). Create and Symlink don't apply this second pass — Mkdir was the odd one out. The resulting dirs had fewer write bits than the caller asked for, which broke cross-user rename permission checks (kernel may_delete rejects with EACCES when the parent lacks o+w even though the caller explicitly requested it) and blocked pjdfstest tests/rename/21.t and its cascading checks. Drop the extra umask so Mkdir trusts in.Mode exactly like Create. The CLI -umask flag still covers the internal cache dirs that the mount creates for itself via os.MkdirAll; only the user-facing Mkdir path changes. Unblocks tests/rename/21.t — full pjdfstest suite is now 236 files / 8819 tests, all PASS, and known_failures.txt is empty.
169 lines
5.1 KiB
Go
169 lines
5.1 KiB
Go
package mount
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/seaweedfs/go-fuse/v2/fuse"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/filer"
|
|
"github.com/seaweedfs/seaweedfs/weed/glog"
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
|
|
)
|
|
|
|
/** Create a directory
|
|
*
|
|
* Note that the mode argument may not have the type specification
|
|
* bits set, i.e. S_ISDIR(mode) can be false. To obtain the
|
|
* correct directory type bits use mode|S_IFDIR
|
|
* */
|
|
func (wfs *WFS) Mkdir(cancel <-chan struct{}, in *fuse.MkdirIn, name string, out *fuse.EntryOut) (code fuse.Status) {
|
|
|
|
if wfs.IsOverQuotaWithUncommitted() {
|
|
return fuse.Status(syscall.ENOSPC)
|
|
}
|
|
|
|
if s := checkName(name); s != fuse.OK {
|
|
return s
|
|
}
|
|
|
|
now := time.Now().Unix()
|
|
newEntry := &filer_pb.Entry{
|
|
Name: name,
|
|
IsDirectory: true,
|
|
Attributes: &filer_pb.FuseAttributes{
|
|
Mtime: now,
|
|
Crtime: now,
|
|
Ctime: now,
|
|
FileMode: uint32(os.ModeDir) | in.Mode,
|
|
Uid: in.Uid,
|
|
Gid: in.Gid,
|
|
},
|
|
}
|
|
|
|
dirFullPath, code := wfs.inodeToPath.GetPath(in.NodeId)
|
|
if code != fuse.OK {
|
|
return
|
|
}
|
|
|
|
entryFullPath := dirFullPath.Child(name)
|
|
|
|
wfs.mapPbIdFromLocalToFiler(newEntry)
|
|
// Defer restoring to local uid/gid AFTER the entry is sent to the filer
|
|
// but BEFORE outputPbEntry writes attributes to the kernel. We restore
|
|
// explicitly below instead of using defer so the kernel gets local values.
|
|
|
|
request := &filer_pb.CreateEntryRequest{
|
|
Directory: string(dirFullPath),
|
|
Entry: newEntry,
|
|
Signatures: []int32{wfs.signature},
|
|
SkipCheckParentDirectory: true,
|
|
}
|
|
|
|
glog.V(1).Infof("mkdir: %v", request)
|
|
resp, err := wfs.streamCreateEntry(context.Background(), request)
|
|
if err != nil {
|
|
glog.V(0).Infof("mkdir %s: %v", entryFullPath, err)
|
|
} else {
|
|
event := resp.GetMetadataEvent()
|
|
if event == nil {
|
|
event = metadataCreateEvent(string(dirFullPath), newEntry)
|
|
}
|
|
if applyErr := wfs.applyLocalMetadataEvent(context.Background(), event); applyErr != nil {
|
|
glog.Warningf("mkdir %s: best-effort metadata apply failed: %v", entryFullPath, applyErr)
|
|
wfs.inodeToPath.InvalidateChildrenCache(dirFullPath)
|
|
}
|
|
wfs.inodeToPath.TouchDirectory(dirFullPath)
|
|
wfs.touchDirMtimeCtimeBest(dirFullPath)
|
|
wfs.inodeToPath.AdjustSubdirCount(dirFullPath, 1)
|
|
}
|
|
|
|
glog.V(3).Infof("mkdir %s: %v", entryFullPath, err)
|
|
|
|
if err != nil {
|
|
wfs.mapPbIdFromFilerToLocal(newEntry)
|
|
return fuse.EIO
|
|
}
|
|
|
|
// Map uid/gid back to local-space before writing attributes to the
|
|
// kernel. The kernel (especially macFUSE) caches these and uses them
|
|
// for subsequent permission checks on children.
|
|
wfs.mapPbIdFromFilerToLocal(newEntry)
|
|
|
|
inode := wfs.inodeToPath.Lookup(entryFullPath, newEntry.Attributes.Crtime, true, false, 0, true)
|
|
|
|
// The newly created directory is guaranteed to be empty, so mark it as
|
|
// cached immediately to avoid a needless filer round-trip on the first
|
|
// Lookup or ReadDir inside this directory.
|
|
wfs.inodeToPath.MarkChildrenCached(entryFullPath)
|
|
|
|
wfs.outputPbEntry(out, inode, newEntry)
|
|
|
|
return fuse.OK
|
|
|
|
}
|
|
|
|
/** Remove a directory */
|
|
func (wfs *WFS) Rmdir(cancel <-chan struct{}, header *fuse.InHeader, name string) (code fuse.Status) {
|
|
|
|
if name == "." {
|
|
return fuse.Status(syscall.EINVAL)
|
|
}
|
|
if name == ".." {
|
|
return fuse.Status(syscall.ENOTEMPTY)
|
|
}
|
|
|
|
dirFullPath, code := wfs.inodeToPath.GetPath(header.NodeId)
|
|
if code != fuse.OK {
|
|
return
|
|
}
|
|
entryFullPath := dirFullPath.Child(name)
|
|
|
|
// POSIX: enforce sticky bit on the parent directory.
|
|
if dirEntry, dirCode := wfs.maybeLoadEntry(dirFullPath); dirCode == fuse.OK && dirEntry != nil && dirEntry.Attributes != nil {
|
|
targetUid := uint32(0)
|
|
if targetEntry, targetCode := wfs.maybeLoadEntry(entryFullPath); targetCode == fuse.OK && targetEntry != nil && targetEntry.Attributes != nil {
|
|
targetUid = targetEntry.Attributes.Uid
|
|
}
|
|
if code := checkStickyBit(dirEntry.Attributes.FileMode, dirEntry.Attributes.Uid, targetUid, header.Uid); code != fuse.OK {
|
|
return code
|
|
}
|
|
}
|
|
|
|
glog.V(3).Infof("remove directory: %v", entryFullPath)
|
|
deleteReq := &filer_pb.DeleteEntryRequest{
|
|
Directory: string(dirFullPath),
|
|
Name: name,
|
|
IsDeleteData: true,
|
|
IgnoreRecursiveError: true, // ignore recursion error since the OS should manage it
|
|
Signatures: []int32{wfs.signature},
|
|
}
|
|
resp, err := wfs.streamDeleteEntry(context.Background(), deleteReq)
|
|
if err != nil {
|
|
glog.V(1).Infof("remove %s: %v", entryFullPath, err)
|
|
if strings.Contains(err.Error(), filer.MsgFailDelNonEmptyFolder) {
|
|
return fuse.Status(syscall.ENOTEMPTY)
|
|
}
|
|
return fuse.ENOENT
|
|
}
|
|
|
|
event := metadataDeleteEvent(string(dirFullPath), name, true)
|
|
if resp != nil && resp.MetadataEvent != nil {
|
|
event = resp.MetadataEvent
|
|
}
|
|
if applyErr := wfs.applyLocalMetadataEvent(context.Background(), event); applyErr != nil {
|
|
glog.Warningf("rmdir %s: best-effort metadata apply failed: %v", entryFullPath, applyErr)
|
|
wfs.inodeToPath.InvalidateChildrenCache(dirFullPath)
|
|
}
|
|
wfs.inodeToPath.RemovePath(entryFullPath)
|
|
wfs.inodeToPath.TouchDirectory(dirFullPath)
|
|
wfs.touchDirMtimeCtimeBest(dirFullPath)
|
|
wfs.inodeToPath.AdjustSubdirCount(dirFullPath, -1)
|
|
|
|
return fuse.OK
|
|
|
|
}
|