Example #1
0
// DeleteLongName deletes "hashName.name".
func DeleteLongName(dirfd *os.File, hashName string) error {
	err := syscallcompat.Unlinkat(int(dirfd.Fd()), hashName+LongNameSuffix)
	if err != nil {
		tlog.Warn.Printf("DeleteLongName: %v", err)
	}
	return err
}
Example #2
0
// 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)
}
Example #3
0
// Rmdir implements pathfs.FileSystem
func (fs *FS) Rmdir(path string, context *fuse.Context) (code fuse.Status) {
	cPath, err := fs.getBackingPath(path)
	if err != nil {
		return fuse.ToStatus(err)
	}
	if fs.args.PlaintextNames {
		err = syscall.Rmdir(cPath)
		return fuse.ToStatus(err)
	}
	parentDir := filepath.Dir(cPath)
	parentDirFd, err := os.Open(parentDir)
	if err != nil {
		return fuse.ToStatus(err)
	}
	defer parentDirFd.Close()

	cName := filepath.Base(cPath)
	dirfdRaw, err := syscallcompat.Openat(int(parentDirFd.Fd()), cName,
		syscall.O_RDONLY, 0)
	if err == syscall.EACCES {
		// We need permission to read and modify the directory
		tlog.Debug.Printf("Rmdir: handling EACCESS")
		// TODO use syscall.Fstatat once it is available in Go
		var fi os.FileInfo
		fi, err = os.Lstat(cPath)
		if err != nil {
			tlog.Debug.Printf("Rmdir: Stat: %v", err)
			return fuse.ToStatus(err)
		}
		origMode := fi.Mode()
		// TODO use syscall.Chmodat once it is available in Go
		err = os.Chmod(cPath, origMode|0700)
		if err != nil {
			tlog.Debug.Printf("Rmdir: Chmod failed: %v", err)
			return fuse.ToStatus(err)
		}
		// Retry open
		var st syscall.Stat_t
		syscall.Lstat(cPath, &st)
		dirfdRaw, err = syscallcompat.Openat(int(parentDirFd.Fd()), cName,
			syscall.O_RDONLY, 0)
		// Undo the chmod if removing the directory failed
		defer func() {
			if code != fuse.OK {
				err = os.Chmod(cPath, origMode)
				if err != nil {
					tlog.Warn.Printf("Rmdir: Chmod rollback failed: %v", err)
				}
			}
		}()
	}
	if err != nil {
		tlog.Debug.Printf("Rmdir: Open: %v", err)
		return fuse.ToStatus(err)
	}
	dirfd := os.NewFile(uintptr(dirfdRaw), cName)
	defer dirfd.Close()

	children, err := dirfd.Readdirnames(10)
	if err == nil {
		// If the directory is not empty besides gocryptfs.diriv, do not even
		// attempt the dance around gocryptfs.diriv.
		if len(children) > 1 {
			return fuse.ToStatus(syscall.ENOTEMPTY)
		}
		// Move "gocryptfs.diriv" to the parent dir as "gocryptfs.diriv.rmdir.XYZ"
		tmpName := fmt.Sprintf("gocryptfs.diriv.rmdir.%d", cryptocore.RandUint64())
		tlog.Debug.Printf("Rmdir: Renaming %s to %s", nametransform.DirIVFilename, tmpName)
		// The directory is in an inconsistent state between rename and rmdir.
		// Protect against concurrent readers.
		fs.dirIVLock.Lock()
		defer fs.dirIVLock.Unlock()
		err = syscallcompat.Renameat(int(dirfd.Fd()), nametransform.DirIVFilename,
			int(parentDirFd.Fd()), tmpName)
		if err != nil {
			tlog.Warn.Printf("Rmdir: Renaming %s to %s failed: %v",
				nametransform.DirIVFilename, tmpName, err)
			return fuse.ToStatus(err)
		}
		// Actual Rmdir
		// TODO Use syscall.Unlinkat with the AT_REMOVEDIR flag once it is available
		// in Go
		err = syscall.Rmdir(cPath)
		if err != nil {
			// This can happen if another file in the directory was created in the
			// meantime, undo the rename
			err2 := syscallcompat.Renameat(int(parentDirFd.Fd()), tmpName,
				int(dirfd.Fd()), nametransform.DirIVFilename)
			if err != nil {
				tlog.Warn.Printf("Rmdir: Rename rollback failed: %v", err2)
			}
			return fuse.ToStatus(err)
		}
		// Delete "gocryptfs.diriv.rmdir.XYZ"
		err = syscallcompat.Unlinkat(int(parentDirFd.Fd()), tmpName)
		if err != nil {
			tlog.Warn.Printf("Rmdir: Could not clean up %s: %v", tmpName, err)
		}
	} else if err == io.EOF {
		// The directory is empty
		tlog.Warn.Printf("Rmdir: %q: gocryptfs.diriv is missing", cPath)
		err = syscall.Rmdir(cPath)
		if err != nil {
			return fuse.ToStatus(err)
		}
	} else {
		tlog.Warn.Printf("Rmdir: Readdirnames: %v", err)
		return fuse.ToStatus(err)
	}
	// Delete .name file
	if nametransform.IsLongContent(cName) {
		nametransform.DeleteLongName(parentDirFd, cName)
	}
	// The now-deleted directory may have been in the DirIV cache. Clear it.
	fs.nameTransform.DirIVCache.Clear()
	return fuse.OK
}