Give FUSE hard link capabilities and enable the link syscall test.

PiperOrigin-RevId: 507001947
This commit is contained in:
Lucas Manning
2023-02-03 15:00:04 -08:00
committed by gVisor bot
parent af99fcb280
commit fc52a6d9cc
11 changed files with 103 additions and 20 deletions
+26
View File
@@ -755,6 +755,32 @@ func (r *FUSESymlinkIn) SizeBytes() int {
return r.Name.SizeBytes() + r.Target.SizeBytes()
}
// FUSELinkIn is the request sent by the kernel to create a hard link.
//
// +marshal dynamic
type FUSELinkIn struct {
// OldNodeID is the ID of the inode that is being linked to.
OldNodeID primitive.Uint64
// Name of the new hard link to create.
Name CString
}
// MarshalBytes implements marshal.Marshallable.MarshalBytes.
func (r *FUSELinkIn) MarshalBytes(buf []byte) []byte {
buf = r.OldNodeID.MarshalBytes(buf)
return r.Name.MarshalBytes(buf)
}
// UnmarshalBytes implements marshal.Marshallable.UnmarshalBytes.
func (r *FUSELinkIn) UnmarshalBytes(buf []byte) []byte {
panic("Unimplemented, FUSELinkIn is never unmarshalled")
}
// SizeBytes implements marshal.Marshallable.SizeBytes.
func (r *FUSELinkIn) SizeBytes() int {
return r.OldNodeID.SizeBytes() + r.Name.SizeBytes()
}
// FUSEEmptyIn is used by operations without request body.
//
// +marshal dynamic
+2 -2
View File
@@ -291,7 +291,7 @@ func (fs *filesystem) MountOptions() string {
func (fs *filesystem) newRoot(ctx context.Context, creds *auth.Credentials, mode linux.FileMode) *kernfs.Dentry {
i := &inode{fs: fs, nodeID: 1}
i.attrMu.Lock()
i.init(creds, linux.UNNAMED_MAJOR, fs.devMinor, 1, linux.ModeDirectory|0755)
i.init(creds, linux.UNNAMED_MAJOR, fs.devMinor, 1, linux.ModeDirectory|0755, 2)
i.attrMu.Unlock()
i.OrderedChildren.Init(kernfs.OrderedChildrenOptions{})
i.InitRefs()
@@ -305,7 +305,7 @@ func (fs *filesystem) newInode(ctx context.Context, nodeID uint64, attr linux.FU
i := &inode{fs: fs, nodeID: nodeID}
creds := auth.Credentials{EffectiveKGID: auth.KGID(attr.UID), EffectiveKUID: auth.KUID(attr.UID)}
i.attrMu.Lock()
i.init(&creds, linux.UNNAMED_MAJOR, fs.devMinor, fs.NextIno(), linux.FileMode(attr.Mode))
i.init(&creds, linux.UNNAMED_MAJOR, fs.devMinor, nodeID, linux.FileMode(attr.Mode), attr.Nlink)
i.size.Store(attr.Size)
i.attrMu.Unlock()
i.OrderedChildren.Init(kernfs.OrderedChildrenOptions{})
+23 -6
View File
@@ -45,7 +45,6 @@ type fileHandle struct {
type inode struct {
inodeRefs
kernfs.InodeAlwaysValid
kernfs.InodeDirectoryNoNewChildren
kernfs.InodeNotSymlink
kernfs.InodeWatches
kernfs.OrderedChildren
@@ -110,6 +109,18 @@ func (i *inode) Mode() linux.FileMode {
return i.filemode()
}
func (i *inode) UID() auth.KUID {
i.attrMu.Lock()
defer i.attrMu.Unlock()
return auth.KUID(i.uid.Load())
}
func (i *inode) GID() auth.KGID {
i.attrMu.Lock()
defer i.attrMu.Unlock()
return auth.KGID(i.gid.Load())
}
// +checklocks:i.attrMu
func (i *inode) filemode() linux.FileMode {
return linux.FileMode(i.mode.Load())
@@ -132,15 +143,11 @@ func (i *inode) touchAtime() {
}
// +checklocks:i.attrMu
func (i *inode) init(creds *auth.Credentials, devMajor, devMinor uint32, nodeid uint64, mode linux.FileMode) {
func (i *inode) init(creds *auth.Credentials, devMajor, devMinor uint32, nodeid uint64, mode linux.FileMode, nlink uint32) {
if mode.FileType() == 0 {
panic(fmt.Sprintf("No file type specified in 'mode' for InodeAttrs.Init(): mode=0%o", mode))
}
nlink := uint32(1)
if mode.FileType() == linux.ModeDirectory {
nlink = 2
}
i.nodeID = nodeid
i.ino.Store(nodeid)
i.mode.Store(uint32(mode))
@@ -399,6 +406,16 @@ func (i *inode) NewSymlink(ctx context.Context, name, target string) (kernfs.Ino
return i.newEntry(ctx, name, linux.S_IFLNK, linux.FUSE_SYMLINK, &in)
}
// NewLink implements kernfs.Inode.NewLink.
func (i *inode) NewLink(ctx context.Context, name string, target kernfs.Inode) (kernfs.Inode, error) {
targetInode := target.(*inode)
in := linux.FUSELinkIn{
OldNodeID: primitive.Uint64(targetInode.nodeID),
Name: linux.CString(name),
}
return i.newEntry(ctx, name, targetInode.Mode().FileType(), linux.FUSE_LINK, &in)
}
// Unlink implements kernfs.Inode.Unlink.
func (i *inode) Unlink(ctx context.Context, name string, child kernfs.Inode) error {
kernelTask := kernel.TaskFromContext(ctx)
+3 -4
View File
@@ -108,9 +108,8 @@ func (conn *connection) NewRequest(creds *auth.Credentials, pid uint32, ino uint
defer conn.fd.mu.Unlock()
conn.fd.nextOpID += linux.FUSEOpID(reqIDStep)
hdrLen := (*linux.FUSEHeaderIn)(nil).SizeBytes()
hdr := linux.FUSEHeaderIn{
Len: uint32(hdrLen + payload.SizeBytes()),
Len: linux.SizeOfFUSEHeaderIn + uint32(payload.SizeBytes()),
Opcode: opcode,
Unique: conn.fd.nextOpID,
NodeID: ino,
@@ -121,8 +120,8 @@ func (conn *connection) NewRequest(creds *auth.Credentials, pid uint32, ino uint
buf := make([]byte, hdr.Len)
hdr.MarshalUnsafe(buf[:hdrLen])
payload.MarshalUnsafe(buf[hdrLen:])
hdr.MarshalUnsafe(buf[:linux.SizeOfFUSEHeaderIn])
payload.MarshalUnsafe(buf[linux.SizeOfFUSEHeaderIn:])
return &Request{
id: hdr.Unique,
+10
View File
@@ -346,6 +346,16 @@ func (i *inode) Mode() linux.FileMode {
return linux.FileMode(s.Mode)
}
// Mode implements kernfs.Inode.UID
func (i *inode) UID() auth.KUID {
return auth.KUID(i.virtualOwner.uid.Load())
}
// Mode implements kernfs.Inode.GID
func (i *inode) GID() auth.KGID {
return auth.KGID(i.virtualOwner.gid.Load())
}
// Stat implements kernfs.Inode.Stat.
func (i *inode) Stat(ctx context.Context, vfsfs *vfs.Filesystem, opts vfs.StatOptions) (linux.Statx, error) {
if opts.Mask&linux.STATX__RESERVED != 0 {
+9 -7
View File
@@ -364,6 +364,13 @@ func (fs *Filesystem) LinkAt(ctx context.Context, rp *vfs.ResolvingPath, vd vfs.
parent.dirMu.Lock()
defer parent.dirMu.Unlock()
inode := vd.Dentry().Impl().(*Dentry).Inode()
if inode.Mode().IsDir() {
return linuxerr.EPERM
}
if err := vfs.MayLink(rp.Credentials(), inode.Mode(), inode.UID(), inode.GID()); err != nil {
return err
}
pc := rp.Component()
if err := checkCreateLocked(ctx, rp.Credentials(), pc, parent); err != nil {
return err
@@ -379,17 +386,12 @@ func (fs *Filesystem) LinkAt(ctx context.Context, rp *vfs.ResolvingPath, vd vfs.
}
defer rp.Mount().EndWrite()
d := vd.Dentry().Impl().(*Dentry)
if d.isDir() {
return linuxerr.EPERM
}
childI, err := parent.inode.NewLink(ctx, pc, d.inode)
childI, err := parent.inode.NewLink(ctx, pc, inode)
if err != nil {
return err
}
parent.inode.Watches().Notify(ctx, pc, linux.IN_CREATE, 0, vfs.InodeEvent, false /* unlinked */)
d.inode.Watches().Notify(ctx, "", linux.IN_ATTRIB, 0, vfs.InodeEvent, false /* unlinked */)
inode.Watches().Notify(ctx, "", linux.IN_ATTRIB, 0, vfs.InodeEvent, false /* unlinked */)
var child Dentry
child.Init(fs, childI)
parent.insertChildLocked(pc, &child)
@@ -228,6 +228,16 @@ func (a *InodeAttrs) Ino() uint64 {
return a.ino.Load()
}
// UID implements Inode.UID.
func (a *InodeAttrs) UID() auth.KUID {
return auth.KUID(a.uid.Load())
}
// GID implements Inode.GID.
func (a *InodeAttrs) GID() auth.KGID {
return auth.KGID(a.gid.Load())
}
// Mode implements Inode.Mode.
func (a *InodeAttrs) Mode() linux.FileMode {
return linux.FileMode(a.mode.Load())
+8
View File
@@ -722,6 +722,14 @@ type inodeMetadata interface {
// separated from Stat for performance.
Mode() linux.FileMode
// UID returns the (struct stat)::st_uid value for this inode. This is
// separated from Stat for performance.
UID() auth.KUID
// GID returns the (struct stat)::st_gid value for this inode. This is
// separated from Stat for performance.
GID() auth.KGID
// Stat returns the metadata for this inode. This corresponds to
// vfs.FilesystemImpl.StatAt.
Stat(ctx context.Context, fs *vfs.Filesystem, opts vfs.StatOptions) (linux.Statx, error)
+10
View File
@@ -127,6 +127,16 @@ func (i *inode) Mode() linux.FileMode {
return pipeMode
}
// UID implements kernfs.Inode.UID.
func (i *inode) UID() auth.KUID {
return auth.KUID(i.uid)
}
// GID implements kernfs.Inode.GID.
func (i *inode) GID() auth.KGID {
return auth.KGID(i.gid)
}
// Stat implements kernfs.Inode.Stat.
func (i *inode) Stat(_ context.Context, vfsfs *vfs.Filesystem, opts vfs.StatOptions) (linux.Statx, error) {
ts := linux.NsecToStatxTimestamp(i.ctime.Nanoseconds())
+1 -1
View File
@@ -34,7 +34,7 @@ func main() {
log.Warningf("could not create loopback root: %v", err)
os.Exit(1)
}
opts := &fuse.MountOptions{DirectMount: true, Debug: true, Options: []string{"default_permissions"}}
opts := &fuse.MountOptions{DirectMount: true, Debug: true, AllowOther: true, Options: []string{"default_permissions"}}
rawFS := fs.NewNodeFS(loopbackRoot, &fs.Options{NullPermissions: true, Logger: golog.Default()})
server, err := fuse.NewServer(rawFS, "/tmp", opts)
if err != nil {
+1
View File
@@ -293,6 +293,7 @@ syscall_test(
)
syscall_test(
add_fusefs = True,
add_overlay = True,
test = "//test/syscalls/linux:link_test",
use_tmpfs = True, # gofer needs CAP_DAC_READ_SEARCH to use AT_EMPTY_PATH with linkat(2)