func (constor *Constor) Open(input *fuse.OpenIn, out *fuse.OpenOut) (status fuse.Status) { pathl, err := constor.getPath(input.NodeId) if err != nil { constor.error("%s", err) return fuse.ToStatus(err) } inode, err := constor.inodemap.findInode(input.NodeId) if err != nil { constor.error("%s", err) return fuse.ToStatus(err) } constor.log("%s %d %d %d", pathl, input.Flags, input.Uid, input.Gid) fd, err := syscall.Open(pathl, int(input.Flags), 0) if err != nil { constor.error("%s", err) return fuse.ToStatus(err) } F := new(FD) F.fd = fd F.flags = int(input.Flags) F.layer = inode.layer constor.putfd(F) out.Fh = uint64(uintptr(unsafe.Pointer(F))) out.OpenFlags = 0 constor.log("%d", out.Fh) return fuse.OK }
func TestCreationChecks(t *testing.T) { wd, clean := setup(t) defer clean() err := os.Mkdir(wd+"/store/foo", 0755) CheckSuccess(err) os.Symlink(wd+"/ro", wd+"/store/foo/READONLY") CheckSuccess(err) err = os.Mkdir(wd+"/store/ws2", 0755) CheckSuccess(err) os.Symlink(wd+"/ro", wd+"/store/ws2/READONLY") CheckSuccess(err) err = os.Symlink(wd+"/store/foo", wd+"/mnt/config/bar") CheckSuccess(err) err = os.Symlink(wd+"/store/foo", wd+"/mnt/config/foo") code := fuse.ToStatus(err) if code != fuse.EBUSY { t.Error("Should return EBUSY", err) } err = os.Symlink(wd+"/store/ws2", wd+"/mnt/config/config") code = fuse.ToStatus(err) if code != fuse.EINVAL { t.Error("Should return EINVAL", err) } }
func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (code fuse.Status) { if fs.isFiltered(newPath) { return fuse.EPERM } cOldPath, err := fs.getBackingPath(oldPath) if err != nil { return fuse.ToStatus(err) } cNewPath, err := fs.getBackingPath(newPath) if err != nil { return fuse.ToStatus(err) } // The Rename may cause a directory to take the place of another directory. // That directory may still be in the DirIV cache, clear it. fs.CryptFS.DirIVCacheEnc.Clear() err = os.Rename(cOldPath, cNewPath) if lerr, ok := err.(*os.LinkError); ok && lerr.Err == syscall.ENOTEMPTY { // If an empty directory is overwritten we will always get // ENOTEMPTY as the "empty" directory will still contain gocryptfs.diriv. // Handle that case by removing the target directory and trying again. cryptfs.Debug.Printf("Rename: Handling ENOTEMPTY") if fs.Rmdir(newPath, context) == fuse.OK { err = os.Rename(cOldPath, cNewPath) } } return fuse.ToStatus(err) }
// Unlink implements pathfs.Filesystem. func (fs *FS) Unlink(path string, context *fuse.Context) (code fuse.Status) { if fs.isFiltered(path) { return fuse.EPERM } cPath, err := fs.getBackingPath(path) if err != nil { return fuse.ToStatus(err) } cName := filepath.Base(cPath) if nametransform.IsLongContent(cName) { var dirfd *os.File dirfd, err = os.Open(filepath.Dir(cPath)) if err != nil { return fuse.ToStatus(err) } defer dirfd.Close() // Delete content err = syscallcompat.Unlinkat(int(dirfd.Fd()), cName) if err != nil { return fuse.ToStatus(err) } // Delete ".name" err = nametransform.DeleteLongName(dirfd, cName) if err != nil { tlog.Warn.Printf("Unlink: could not delete .name file: %v", err) } return fuse.ToStatus(err) } err = syscall.Unlink(cPath) return fuse.ToStatus(err) }
// implements nodefs.File func (this *hookFile) Fsync(flags int) fuse.Status { hook, hookEnabled := this.hook.(HookOnFsync) var prehookErr, posthookErr error var prehooked, posthooked bool var prehookCtx HookContext if hookEnabled { prehookErr, prehooked, prehookCtx = hook.PreFsync(this.name, uint32(flags)) if prehooked { log.WithFields(log.Fields{ "this": this, "prehookErr": prehookErr, "prehookCtx": prehookCtx, }).Debug("Fsync: Prehooked") return fuse.ToStatus(prehookErr) } } lowerCode := this.file.Fsync(flags) if hookEnabled { posthookErr, posthooked = hook.PostFsync(int32(lowerCode), prehookCtx) if posthooked { log.WithFields(log.Fields{ "this": this, "posthookErr": posthookErr, }).Debug("Fsync: Posthooked") return fuse.ToStatus(posthookErr) } } return lowerCode }
func (fs *FS) Symlink(target string, linkName string, context *fuse.Context) (code fuse.Status) { cryptfs.Debug.Printf("Symlink(\"%s\", \"%s\")", target, linkName) if fs.isFiltered(linkName) { return fuse.EPERM } cPath, err := fs.getBackingPath(linkName) if err != nil { return fuse.ToStatus(err) } // Old filesystem: symlinks are encrypted like paths (CBC) if !fs.args.DirIV { cTarget, err := fs.encryptPath(target) if err != nil { cryptfs.Warn.Printf("Symlink: BUG: we should not get an error here: %v", err) return fuse.ToStatus(err) } err = os.Symlink(cTarget, cPath) return fuse.ToStatus(err) } // Since gocryptfs v0.5 symlinks are encrypted like file contents (GCM) cBinTarget := fs.CryptFS.EncryptBlock([]byte(target), 0, nil) cTarget := base64.URLEncoding.EncodeToString(cBinTarget) err = os.Symlink(cTarget, cPath) cryptfs.Debug.Printf("Symlink: os.Symlink(%s, %s) = %v", cTarget, cPath, err) return fuse.ToStatus(err) }
func (constor *Constor) Create(input *fuse.CreateIn, name string, out *fuse.CreateOut) (code fuse.Status) { dirpath, err := constor.dentrymap.getPath(input.NodeId) if err != nil { constor.error("%s", err) return fuse.ToStatus(err) } constor.log("%s%s %d %d %d", dirpath, name, input.Mode, input.Uid, input.Gid) if err := constor.createPath(dirpath); err != nil { constor.error("%s", err) return fuse.ToStatus(err) } pathl := Path.Join(constor.layers[0], dirpath, name) // remove any deleted place holder entries if constor.isdeleted(pathl) { syscall.Unlink(pathl) } fd, err := syscall.Open(pathl, syscall.O_CREAT|syscall.O_RDWR, input.Mode) if err != nil { constor.error("%s", err) return fuse.ToStatus(err) } err = syscall.Chown(pathl, int(input.Uid), int(input.Gid)) if err != nil { constor.error("%s", err) return fuse.ToStatus(err) } F := new(FD) F.fd = fd F.layer = 0 constor.putfd(F) out.Fh = uint64(uintptr(unsafe.Pointer(F))) constor.log("%d", out.Fh) return constor.Lookup((*fuse.InHeader)(unsafe.Pointer(input)), name, &out.EntryOut) }
func (rfs *ReverseFS) inoAwareStat(relPlainPath string) (*fuse.Attr, fuse.Status) { absPath, err := rfs.abs(relPlainPath, nil) if err != nil { return nil, fuse.ToStatus(err) } var fi os.FileInfo if relPlainPath == "" { // Look through symlinks for the root dir fi, err = os.Stat(absPath) } else { fi, err = os.Lstat(absPath) } if err != nil { return nil, fuse.ToStatus(err) } st := fi.Sys().(*syscall.Stat_t) // The file has hard links. We have to give it a stable inode number so // tar or rsync can find them. if fi.Mode().IsRegular() && st.Nlink > 1 { di := fusefrontend.DevInoFromStat(st) rfs.inoMapLock.Lock() stableIno := rfs.inoMap[di] if stableIno == 0 { rfs.inoMap[di] = rfs.inoGen.next() } rfs.inoMapLock.Unlock() st.Ino = stableIno } else { st.Ino = rfs.inoGen.next() } a := &fuse.Attr{} a.FromStat(st) return a, fuse.OK }
// Readlink implements pathfs.Filesystem. func (fs *FS) Readlink(path string, context *fuse.Context) (out string, status fuse.Status) { cPath, err := fs.getBackingPath(path) if err != nil { return "", fuse.ToStatus(err) } cTarget, err := os.Readlink(cPath) if err != nil { return "", fuse.ToStatus(err) } if fs.args.PlaintextNames { return cTarget, fuse.OK } // Symlinks are encrypted like file contents (GCM) and base64-encoded cBinTarget, err := base64.URLEncoding.DecodeString(cTarget) if err != nil { tlog.Warn.Printf("Readlink: %v", err) return "", fuse.EIO } target, err := fs.contentEnc.DecryptBlock([]byte(cBinTarget), 0, nil) if err != nil { tlog.Warn.Printf("Readlink: %v", err) return "", fuse.EIO } return string(target), fuse.OK }
func (constor *Constor) Access(input *fuse.AccessIn) (code fuse.Status) { constor.log("%d", input.NodeId) // FIXME: oops fix this path, err := constor.getPath(input.NodeId) if err != nil { return fuse.ToStatus(err) } return fuse.ToStatus(syscall.Access(path, input.Mask)) }
// doRead - returns "length" plaintext bytes from plaintext offset "off". // Arguments "length" and "off" do not have to be block-aligned. // // doRead reads the corresponding ciphertext blocks from disk, decrypts them and // returns the requested part of the plaintext. // // Called by Read() for normal reading, // by Write() and Truncate() for Read-Modify-Write func (f *file) doRead(off uint64, length uint64) ([]byte, fuse.Status) { // Read file header if f.header == nil { err := f.readHeader() if err == io.EOF { return nil, fuse.OK } if err != nil { return nil, fuse.ToStatus(err) } } // Read the backing ciphertext in one go blocks := f.cfs.ExplodePlainRange(off, length) alignedOffset, alignedLength := blocks[0].JointCiphertextRange(blocks) skip := blocks[0].Skip cryptfs.Debug.Printf("JointCiphertextRange(%d, %d) -> %d, %d, %d\n", off, length, alignedOffset, alignedLength, skip) ciphertext := make([]byte, int(alignedLength)) f.fdLock.Lock() n, err := f.fd.ReadAt(ciphertext, int64(alignedOffset)) f.fdLock.Unlock() if err != nil && err != io.EOF { cryptfs.Warn.Printf("read: ReadAt: %s\n", err.Error()) return nil, fuse.ToStatus(err) } // Truncate ciphertext buffer down to actually read bytes ciphertext = ciphertext[0:n] firstBlockNo := blocks[0].BlockNo cryptfs.Debug.Printf("ReadAt offset=%d bytes (%d blocks), want=%d, got=%d\n", alignedOffset, firstBlockNo, alignedLength, n) // Decrypt it plaintext, err := f.cfs.DecryptBlocks(ciphertext, firstBlockNo, f.header.Id) if err != nil { curruptBlockNo := firstBlockNo + f.cfs.PlainOffToBlockNo(uint64(len(plaintext))) cipherOff := f.cfs.BlockNoToCipherOff(curruptBlockNo) plainOff := f.cfs.BlockNoToPlainOff(curruptBlockNo) cryptfs.Warn.Printf("ino%d: doRead: corrupt block #%d (plainOff=%d, cipherOff=%d)\n", f.ino, curruptBlockNo, plainOff, cipherOff) return nil, fuse.EIO } // Crop down to the relevant part var out []byte lenHave := len(plaintext) lenWant := int(skip + length) if lenHave > lenWant { out = plaintext[skip:lenWant] } else if lenHave > int(skip) { out = plaintext[skip:lenHave] } // else: out stays empty, file was smaller than the requested offset return out, fuse.OK }
func (fs *FS) Unlink(path string, context *fuse.Context) (code fuse.Status) { if fs.isFiltered(path) { return fuse.EPERM } cPath, err := fs.getBackingPath(path) if err != nil { return fuse.ToStatus(err) } return fuse.ToStatus(syscall.Unlink(cPath)) }
func (fs *FS) Access(path string, mode uint32, context *fuse.Context) (code fuse.Status) { if fs.isFiltered(path) { return fuse.EPERM } cPath, err := fs.getBackingPath(path) if err != nil { return fuse.ToStatus(err) } return fuse.ToStatus(syscall.Access(cPath, mode)) }
// Chown implements pathfs.Filesystem. func (fs *FS) Chown(path string, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) { if fs.isFiltered(path) { return fuse.EPERM } cPath, err := fs.getBackingPath(path) if err != nil { return fuse.ToStatus(err) } return fuse.ToStatus(os.Lchown(cPath, int(uid), int(gid))) }
// Allocate - FUSE call for fallocate(2) // // mode=FALLOC_FL_KEEP_SIZE is implemented directly. // // mode=FALLOC_DEFAULT is implemented as a two-step process: // // (1) Allocate the space using FALLOC_FL_KEEP_SIZE // (2) Set the file size using ftruncate (via truncateGrowFile) // // This allows us to reuse the file grow mechanics from Truncate as they are // complicated and hard to get right. // // Other modes (hole punching, zeroing) are not supported. func (f *file) Allocate(off uint64, sz uint64, mode uint32) fuse.Status { if mode != FALLOC_DEFAULT && mode != FALLOC_FL_KEEP_SIZE { f := func() { tlog.Warn.Print("fallocate: only mode 0 (default) and 1 (keep size) are supported") } allocateWarnOnce.Do(f) return fuse.Status(syscall.EOPNOTSUPP) } f.fdLock.RLock() defer f.fdLock.RUnlock() if f.released { return fuse.EBADF } f.fileTableEntry.writeLock.Lock() defer f.fileTableEntry.writeLock.Unlock() blocks := f.contentEnc.ExplodePlainRange(off, sz) firstBlock := blocks[0] lastBlock := blocks[len(blocks)-1] // Step (1): Allocate the space the user wants using FALLOC_FL_KEEP_SIZE. // This will fill file holes and/or allocate additional space past the end of // the file. cipherOff := firstBlock.BlockCipherOff() cipherSz := lastBlock.BlockCipherOff() - cipherOff + f.contentEnc.PlainSizeToCipherSize(lastBlock.Skip+lastBlock.Length) err := syscallcompat.Fallocate(f.intFd(), FALLOC_FL_KEEP_SIZE, int64(cipherOff), int64(cipherSz)) tlog.Debug.Printf("Allocate off=%d sz=%d mode=%x cipherOff=%d cipherSz=%d\n", off, sz, mode, cipherOff, cipherSz) if err != nil { return fuse.ToStatus(err) } if mode == FALLOC_FL_KEEP_SIZE { // The user did not want to change the apparent size. We are done. return fuse.OK } // Step (2): Grow the apparent file size // We need the old file size to determine if we are growing the file at all. newPlainSz := off + sz oldPlainSz, err := f.statPlainSize() if err != nil { return fuse.ToStatus(err) } if newPlainSz <= oldPlainSz { // The new size is smaller (or equal). Fallocate with mode = 0 never // truncates a file, so we are done. return fuse.OK } // The file grows. The space has already been allocated in (1), so what is // left to do is to pad the first and last block and call truncate. // truncateGrowFile does just that. return f.truncateGrowFile(oldPlainSz, newPlainSz) }
// doWrite - encrypt "data" and write it to plaintext offset "off" // // Arguments do not have to be block-aligned, read-modify-write is // performed internally as neccessary // // Called by Write() for normal writing, // and by Truncate() to rewrite the last file block. func (f *file) doWrite(data []byte, off int64) (uint32, fuse.Status) { // Read header from disk, create a new one if the file is empty if f.header == nil { err := f.readHeader() if err == io.EOF { err = f.createHeader() } if err != nil { return 0, fuse.ToStatus(err) } } var written uint32 status := fuse.OK dataBuf := bytes.NewBuffer(data) blocks := f.cfs.ExplodePlainRange(uint64(off), uint64(len(data))) for _, b := range blocks { blockData := dataBuf.Next(int(b.Length)) // Incomplete block -> Read-Modify-Write if b.IsPartial() { // Read o, _ := b.PlaintextRange() oldData, status := f.doRead(o, f.cfs.PlainBS()) if status != fuse.OK { cryptfs.Warn.Printf("RMW read failed: %s\n", status.String()) return written, status } // Modify blockData = f.cfs.MergeBlocks(oldData, blockData, int(b.Skip)) cryptfs.Debug.Printf("len(oldData)=%d len(blockData)=%d\n", len(oldData), len(blockData)) } // Write blockOffset, _ := b.CiphertextRange() blockData = f.cfs.EncryptBlock(blockData, b.BlockNo, f.header.Id) cryptfs.Debug.Printf("ino%d: Writing %d bytes to block #%d, md5=%s\n", f.ino, len(blockData)-cryptfs.BLOCK_OVERHEAD, b.BlockNo, cryptfs.Debug.Md5sum(blockData)) f.fdLock.Lock() _, err := f.fd.WriteAt(blockData, int64(blockOffset)) f.fdLock.Unlock() if err != nil { cryptfs.Warn.Printf("Write failed: %s\n", err.Error()) status = fuse.ToStatus(err) break } written += uint32(b.Length) } return written, status }
func (constor *Constor) Unlink(header *fuse.InHeader, name string) (code fuse.Status) { var stat syscall.Stat_t parent := constor.inodemap.findInodePtr(header.NodeId) if parent == nil { constor.error("parent == nil") return fuse.ENOENT } constor.log("%s %s", parent.id, name) id, err := constor.getid(-1, parent.id, name) if err != nil { constor.error("getid failed %s %s", parent.id, name) return fuse.ToStatus(err) } inode := constor.inodemap.findInodeId(id) if inode == nil { constor.error("%s %s : inode == nil", parent.id, name) return fuse.ENOENT } if inode.layer == 0 { linkcnt, err := constor.declinkscnt(inode.id) if err != nil { constor.error("declinkscnt %s : %s", inode.id, err) return fuse.ToStatus(err) } if linkcnt == 0 { path := constor.getPath(0, inode.id) if err := syscall.Unlink(path); err != nil { constor.error("Unlink failed for %s : %s", path, err) return fuse.ToStatus(err) } inode.layer = -1 } } err = constor.copyup(parent) if err != nil { constor.error("copyup failed on %s : ", parent.id, err) return fuse.ToStatus(err) } // if there is an entry path, delete it entrypath := Path.Join(constor.getPath(0, parent.id), name) if err := syscall.Lstat(entrypath, &stat); err == nil { if err := syscall.Unlink(entrypath); err != nil { constor.error("Unlink failed for %s : %s", entrypath, err) return fuse.ToStatus(err) } } // if the file is in a lower layer then create a deleted place holder file if _, err := constor.getid(-1, parent.id, name); err == nil { constor.setdeleted(entrypath) } return fuse.OK }
func (b *DirNode) Rmdir(name string, context *fuse.Context) (code fuse.Status) { e := b.Dir.IsChildEmpty(name) if e != nil { return fuse.ToStatus(e) } e = b.Dir.Delete(name, true) if e != nil { return fuse.ToStatus(e) } b.Inode().RmChild(name) return fuse.OK }
func (constor *Constor) Readlink(header *fuse.InHeader) (out []byte, code fuse.Status) { constor.log("%d", header.NodeId) pathl, err := constor.getPath(header.NodeId) if err != nil { return []byte{}, fuse.ToStatus(err) } link, err := os.Readlink(pathl) if err != nil { return []byte{}, fuse.ToStatus(err) } return []byte(link), fuse.OK }
// Create implements pathfs.Filesystem. func (fs *FS) Create(path string, flags uint32, mode uint32, context *fuse.Context) (fuseFile nodefs.File, code fuse.Status) { if fs.isFiltered(path) { return nil, fuse.EPERM } iflags, writeOnly := fs.mangleOpenFlags(flags) cPath, err := fs.getBackingPath(path) if err != nil { return nil, fuse.ToStatus(err) } var fd *os.File cName := filepath.Base(cPath) // Handle long file name if nametransform.IsLongContent(cName) { var dirfd *os.File dirfd, err = os.Open(filepath.Dir(cPath)) if err != nil { return nil, fuse.ToStatus(err) } defer dirfd.Close() // Create ".name" err = fs.nameTransform.WriteLongName(dirfd, cName, path) if err != nil { return nil, fuse.ToStatus(err) } // Create content var fdRaw int fdRaw, err = syscallcompat.Openat(int(dirfd.Fd()), cName, iflags|os.O_CREATE, mode) if err != nil { nametransform.DeleteLongName(dirfd, cName) return nil, fuse.ToStatus(err) } fd = os.NewFile(uintptr(fdRaw), cName) } else { // Normal (short) file name fd, err = os.OpenFile(cPath, iflags|os.O_CREATE, os.FileMode(mode)) if err != nil { return nil, fuse.ToStatus(err) } } // Set owner if fs.args.PreserveOwner { err = fd.Chown(int(context.Owner.Uid), int(context.Owner.Gid)) if err != nil { tlog.Warn.Printf("Create: fd.Chown failed: %v", err) } } return NewFile(fd, writeOnly, fs) }
// Symlink implements pathfs.Filesystem. func (fs *FS) Symlink(target string, linkName string, context *fuse.Context) (code fuse.Status) { tlog.Debug.Printf("Symlink(\"%s\", \"%s\")", target, linkName) if fs.isFiltered(linkName) { return fuse.EPERM } cPath, err := fs.getBackingPath(linkName) if err != nil { return fuse.ToStatus(err) } if fs.args.PlaintextNames { err = os.Symlink(target, cPath) return fuse.ToStatus(err) } // Symlinks are encrypted like file contents (GCM) and base64-encoded cBinTarget := fs.contentEnc.EncryptBlock([]byte(target), 0, nil) cTarget := base64.URLEncoding.EncodeToString(cBinTarget) // Handle long file name cName := filepath.Base(cPath) if nametransform.IsLongContent(cName) { var dirfd *os.File dirfd, err = os.Open(filepath.Dir(cPath)) if err != nil { return fuse.ToStatus(err) } defer dirfd.Close() // Create ".name" file err = fs.nameTransform.WriteLongName(dirfd, cName, linkName) if err != nil { return fuse.ToStatus(err) } // Create "gocryptfs.longfile." symlink // TODO use syscall.Symlinkat once it is available in Go err = syscall.Symlink(cTarget, cPath) if err != nil { nametransform.DeleteLongName(dirfd, cName) } } else { // Create symlink err = os.Symlink(cTarget, cPath) } if err != nil { return fuse.ToStatus(err) } // Set owner if fs.args.PreserveOwner { err = os.Lchown(cPath, int(context.Owner.Uid), int(context.Owner.Gid)) if err != nil { tlog.Warn.Printf("Mknod: Lchown failed: %v", err) } } return fuse.OK }
func (constor *Constor) Write(input *fuse.WriteIn, data []byte) (written uint32, code fuse.Status) { constor.log("%d %d", input.Fh, len(data)) ptr := uintptr(input.Fh) offset := input.Offset wdata := data F := constor.getfd(ptr) if F == nil { constor.error("F == nil") return 0, fuse.EIO } if F.flags&syscall.O_DIRECT != 0 { wdata = directio.AlignedBlock(len(data)) copy(wdata, data) } inode := constor.inodemap.findInodePtr(input.NodeId) if inode == nil { return 0, fuse.ENOENT } if F.layer != 0 && inode.layer != 0 { err := constor.copyup(inode) if err != nil { constor.error("%s", err) return 0, fuse.ToStatus(err) } path := constor.getPath(0, inode.id) syscall.Close(F.fd) fd, err := syscall.Open(path, F.flags, 0) if err != nil { constor.error("%s", err) return 0, fuse.ToStatus(err) } F.fd = fd F.layer = 0 constor.log("reset fd for %s", path) } else if F.layer != 0 && inode.layer == 0 { syscall.Close(F.fd) path := constor.getPath(0, inode.id) fd, err := syscall.Open(path, F.flags, 0) if err != nil { constor.error("%s", err) return 0, fuse.ToStatus(err) } F.fd = fd F.layer = 0 constor.log("reset fd for %s", path) } fd := F.fd n, err := syscall.Pwrite(fd, wdata, int64(offset)) return uint32(n), fuse.ToStatus(err) }
// Access - FUSE call func (rfs *ReverseFS) Access(relPath string, mode uint32, context *fuse.Context) fuse.Status { if rfs.isTranslatedConfig(relPath) { return fuse.OK } if rfs.isDirIV(relPath) { return fuse.OK } absPath, err := rfs.abs(rfs.decryptPath(relPath)) if err != nil { return fuse.ToStatus(err) } return fuse.ToStatus(syscall.Access(absPath, mode)) }
// Chmod implements pathfs.Filesystem. func (fs *FS) Chmod(path string, mode uint32, context *fuse.Context) (code fuse.Status) { if fs.isFiltered(path) { return fuse.EPERM } cPath, err := fs.getBackingPath(path) if err != nil { return fuse.ToStatus(err) } // os.Chmod goes through the "syscallMode" translation function that messes // up the suid and sgid bits. So use syscall.Chmod directly. err = syscall.Chmod(cPath, mode) return fuse.ToStatus(err) }
func (f *AzukiFile) Flush() fuse.Status { f.lock.Lock() newFd, err := syscall.Dup(int(f.File.Fd())) f.lock.Unlock() if err != nil { return fuse.ToStatus(err) } err = syscall.Close(newFd) return fuse.ToStatus(err) }
func (p *pendingFile) rwLoopback() (nodefs.File, fuse.Status) { if p.loopback == nil { if err := p.node.fetch(); err != nil { return nil, fuse.ToStatus(err) } f, err := os.OpenFile(p.node.backing, os.O_RDWR|os.O_CREATE, 0644) if err != nil { return nil, fuse.ToStatus(err) } p.loopback = nodefs.NewLoopbackFile(f) } return p.loopback, fuse.OK }
// implements nodefs.File func (this *hookFile) Read(dest []byte, off int64) (fuse.ReadResult, fuse.Status) { hook, hookEnabled := this.hook.(HookOnRead) var prehookBuf, posthookBuf []byte var prehookErr, posthookErr error var prehooked, posthooked bool var prehookCtx HookContext if hookEnabled { prehookBuf, prehookErr, prehooked, prehookCtx = hook.PreRead(this.name, int64(len(dest)), off) if prehooked { log.WithFields(log.Fields{ "this": this, // "prehookBuf": prehookBuf, "prehookErr": prehookErr, "prehookCtx": prehookCtx, }).Debug("Read: Prehooked") return fuse.ReadResultData(prehookBuf), fuse.ToStatus(prehookErr) } } lowerRR, lowerCode := this.file.Read(dest, off) if hookEnabled { lowerRRBuf, lowerRRBufStatus := lowerRR.Bytes(make([]byte, lowerRR.Size())) if lowerRRBufStatus != fuse.OK { log.WithField("error", lowerRRBufStatus).Panic("lowerRR.Bytes() should not cause an error") } posthookBuf, posthookErr, posthooked = hook.PostRead(int32(lowerCode), lowerRRBuf, prehookCtx) if posthooked { if len(posthookBuf) != len(lowerRRBuf) { log.WithFields(log.Fields{ "this": this, // "posthookBuf": posthookBuf, "posthookErr": posthookErr, "posthookBufLen": len(posthookBuf), "lowerRRBufLen": len(lowerRRBuf), "destLen": len(dest), }).Warn("Read: Posthooked, but posthookBuf length != lowerrRRBuf length. You may get a strange behavior.") } log.WithFields(log.Fields{ "this": this, // "posthookBuf": posthookBuf, "posthookErr": posthookErr, }).Debug("Read: Posthooked") return fuse.ReadResultData(posthookBuf), fuse.ToStatus(posthookErr) } } return lowerRR, lowerCode }
func (b *DirNode) Mkdir(name string, mode uint32, context *fuse.Context) (newNode *nodefs.Inode, code fuse.Status) { d, f, e := b.Dir.Create(name, true) if e != nil { return nil, fuse.ToStatus(e) } if d != nil { dn := &DirNode{nodefs.NewDefaultNode(), d} return b.Inode().NewChild(name, true, dn), fuse.OK } else if f != nil { fn := &FileNode{nodefs.NewDefaultNode(), f} return b.Inode().NewChild(name, false, fn), fuse.OK } return nil, fuse.ToStatus(syscall.EIO) }
func (fs *FS) Link(oldPath string, newPath string, context *fuse.Context) (code fuse.Status) { if fs.isFiltered(newPath) { return fuse.EPERM } cOldPath, err := fs.getBackingPath(oldPath) if err != nil { return fuse.ToStatus(err) } cNewPath, err := fs.getBackingPath(newPath) if err != nil { return fuse.ToStatus(err) } return fuse.ToStatus(os.Link(cOldPath, cNewPath)) }
func (constor *Constor) Lookup(header *fuse.InHeader, name string, out *fuse.EntryOut) fuse.Status { var stat syscall.Stat_t if len(name) > 255 { constor.error("name too long : %s", name) return fuse.Status(syscall.ENAMETOOLONG) } li := -1 parent := constor.inodemap.findInodePtr(header.NodeId) if parent == nil { constor.error("Unable to find parent inode : %d", header.NodeId) return fuse.ENOENT } constor.log("%s(%s)", parent.id, name) id, err := constor.getid(-1, parent.id, name) if err != nil { // logging this error will produce too many logs as there will be too many // lookps on non-existant files return fuse.ToStatus(err) } inode := constor.inodemap.findInodeId(id) if inode != nil { li = inode.layer } else { li = constor.getLayer(id) } if li == -1 { constor.error("Unable to find inode for %s(%s) id %s", parent.id, name, id) return fuse.ENOENT } err = constor.Lstat(li, id, &stat) if err != nil { constor.error("Unable to Lstat inode for %s(%s) id %s", parent.id, name, id) return fuse.ToStatus(err) } if inode == nil { inode = NewInode(constor, id) inode.layer = li constor.inodemap.hashInode(inode) } else { inode.lookup() } attr := (*fuse.Attr)(&out.Attr) attr.FromStat(&stat) out.NodeId = uint64(uintptr(unsafe.Pointer(inode))) out.Ino = attr.Ino out.EntryValid = 1000 out.AttrValid = 1000 constor.log("%s", id) return fuse.OK }