Example #1
0
// WriteLongName encrypts plainName and writes it into "hashName.name".
// For the convenience of the caller, plainName may also be a path and will be
// converted internally.
func (n *NameTransform) WriteLongName(dirfd *os.File, hashName string, plainName string) (err error) {
	plainName = filepath.Base(plainName)

	// Encrypt the basename
	dirIV, err := ReadDirIVAt(dirfd)
	if err != nil {
		return err
	}
	cName := n.EncryptName(plainName, dirIV)

	// Write the encrypted name into hashName.name
	fdRaw, err := syscallcompat.Openat(int(dirfd.Fd()), hashName+LongNameSuffix,
		syscall.O_WRONLY|syscall.O_CREAT|syscall.O_EXCL, 0600)
	if err != nil {
		tlog.Warn.Printf("WriteLongName: Openat: %v", err)
		return err
	}
	fd := os.NewFile(uintptr(fdRaw), hashName+LongNameSuffix)
	defer fd.Close()
	_, err = fd.Write([]byte(cName))
	if err != nil {
		tlog.Warn.Printf("WriteLongName: Write: %v", err)
	}
	return err
}
Example #2
0
// ReadDirIVAt reads "gocryptfs.diriv" from the directory that is opened as "dirfd".
// Using the dirfd makes it immune to concurrent renames of the directory.
func ReadDirIVAt(dirfd *os.File) (iv []byte, err error) {
	fdRaw, err := syscallcompat.Openat(int(dirfd.Fd()), DirIVFilename, syscall.O_RDONLY, 0)
	if err != nil {
		tlog.Warn.Printf("ReadDirIVAt: opening %q in dir %q failed: %v",
			DirIVFilename, dirfd.Name(), err)
		return nil, err
	}
	fd := os.NewFile(uintptr(fdRaw), DirIVFilename)
	defer fd.Close()
	return fdReadDirIV(fd)
}
Example #3
0
// 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)
}
Example #4
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
}