mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Give FUSE hard link capabilities and enable the link syscall test.
PiperOrigin-RevId: 507001947
This commit is contained in:
committed by
gVisor bot
parent
af99fcb280
commit
fc52a6d9cc
@@ -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
|
||||
|
||||
@@ -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{})
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user