2019-07-18 22:09:14 +00:00
|
|
|
// Copyright 2019 The gVisor Authors.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
2020-04-21 19:16:42 +00:00
|
|
|
// Package tmpfs provides an in-memory filesystem whose contents are
|
|
|
|
// application-mutable, consistent with Linux's tmpfs.
|
2019-07-18 22:09:14 +00:00
|
|
|
//
|
|
|
|
// Lock order:
|
|
|
|
//
|
2019-08-08 18:45:33 +00:00
|
|
|
// filesystem.mu
|
|
|
|
// inode.mu
|
2020-02-20 17:57:06 +00:00
|
|
|
// regularFileFD.offMu
|
2020-04-21 19:16:42 +00:00
|
|
|
// *** "memmap.Mappable locks" below this point
|
2020-02-20 17:57:06 +00:00
|
|
|
// regularFile.mapsMu
|
2020-04-21 19:16:42 +00:00
|
|
|
// *** "memmap.Mappable locks taken by Translate" below this point
|
2020-02-20 17:57:06 +00:00
|
|
|
// regularFile.dataMu
|
2020-04-21 19:16:42 +00:00
|
|
|
// directory.iterMu
|
2020-01-06 20:51:35 +00:00
|
|
|
package tmpfs
|
2019-07-18 22:09:14 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2019-12-23 21:17:29 +00:00
|
|
|
"math"
|
2020-06-06 02:10:28 +00:00
|
|
|
"strconv"
|
2020-04-11 02:01:39 +00:00
|
|
|
"strings"
|
2019-07-18 22:09:14 +00:00
|
|
|
"sync/atomic"
|
|
|
|
|
|
|
|
"gvisor.dev/gvisor/pkg/abi/linux"
|
2020-01-27 23:17:58 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/context"
|
2019-07-18 22:09:14 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
2020-01-16 00:31:24 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/sentry/kernel/time"
|
2020-01-06 20:51:35 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/sentry/pgalloc"
|
2019-07-18 22:09:14 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
2020-01-31 22:14:52 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/sentry/vfs/lock"
|
2020-04-11 02:01:39 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/sentry/vfs/memxattr"
|
2020-01-10 06:00:42 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/sync"
|
2020-01-16 21:58:25 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/syserror"
|
2020-04-21 19:16:42 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/usermem"
|
2019-07-18 22:09:14 +00:00
|
|
|
)
|
|
|
|
|
2020-02-14 19:11:55 +00:00
|
|
|
// Name is the default filesystem name.
|
|
|
|
const Name = "tmpfs"
|
|
|
|
|
2019-07-18 22:09:14 +00:00
|
|
|
// FilesystemType implements vfs.FilesystemType.
|
|
|
|
type FilesystemType struct{}
|
|
|
|
|
2019-08-08 18:45:33 +00:00
|
|
|
// filesystem implements vfs.FilesystemImpl.
|
|
|
|
type filesystem struct {
|
2019-07-18 22:09:14 +00:00
|
|
|
vfsfs vfs.Filesystem
|
|
|
|
|
2020-01-06 20:51:35 +00:00
|
|
|
// memFile is used to allocate pages to for regular files.
|
|
|
|
memFile *pgalloc.MemoryFile
|
|
|
|
|
2020-01-16 00:31:24 +00:00
|
|
|
// clock is a realtime clock used to set timestamps in file operations.
|
|
|
|
clock time.Clock
|
|
|
|
|
2020-05-07 21:00:36 +00:00
|
|
|
// devMinor is the filesystem's minor device number. devMinor is immutable.
|
|
|
|
devMinor uint32
|
|
|
|
|
2019-07-18 22:09:14 +00:00
|
|
|
// mu serializes changes to the Dentry tree.
|
|
|
|
mu sync.RWMutex
|
|
|
|
|
|
|
|
nextInoMinusOne uint64 // accessed using atomic memory operations
|
|
|
|
}
|
|
|
|
|
2020-03-27 23:53:28 +00:00
|
|
|
// Name implements vfs.FilesystemType.Name.
|
|
|
|
func (FilesystemType) Name() string {
|
|
|
|
return Name
|
|
|
|
}
|
|
|
|
|
2020-03-31 22:00:25 +00:00
|
|
|
// FilesystemOpts is used to pass configuration data to tmpfs.
|
|
|
|
type FilesystemOpts struct {
|
|
|
|
// RootFileType is the FileType of the filesystem root. Valid values
|
|
|
|
// are: S_IFDIR, S_IFREG, and S_IFLNK. Defaults to S_IFDIR.
|
|
|
|
RootFileType uint16
|
|
|
|
|
|
|
|
// RootSymlinkTarget is the target of the root symlink. Only valid if
|
|
|
|
// RootFileType == S_IFLNK.
|
|
|
|
RootSymlinkTarget string
|
2020-04-01 19:05:17 +00:00
|
|
|
|
|
|
|
// FilesystemType allows setting a different FilesystemType for this
|
|
|
|
// tmpfs filesystem. This allows tmpfs to "impersonate" other
|
|
|
|
// filesystems, like ramdiskfs and cgroupfs.
|
|
|
|
FilesystemType vfs.FilesystemType
|
2020-03-31 22:00:25 +00:00
|
|
|
}
|
|
|
|
|
Minor VFS2 interface changes.
- Remove the Filesystem argument from DentryImpl.*Ref(); in general DentryImpls
that need the Filesystem for reference counting will probably also need it
for other interface methods that don't plumb Filesystem, so it's easier to
just store a pointer to the filesystem in the DentryImpl.
- Add a pointer to the VirtualFilesystem to Filesystem, which is needed by the
gofer client to disown dentries for cache eviction triggered by dentry
reference count changes.
- Rename FilesystemType.NewFilesystem to GetFilesystem; in some cases (e.g.
sysfs, cgroupfs) it's much cleaner for there to be only one Filesystem that
is used by all mounts, and in at least one case (devtmpfs) it's visibly
incorrect not to do so, so NewFilesystem doesn't always actually create and
return a *new* Filesystem.
- Require callers of FileDescription.Init() to increment Mount/Dentry
references. This is because the gofer client may, in the OpenAt() path, take
a reference on a dentry with 0 references, which is safe due to
synchronization that is outside the scope of this CL, and it would be safer
to still have its implementation of DentryImpl.IncRef() check for an
increment for 0 references in other cases.
- Add FileDescription.TryIncRef. This is used by the gofer client to take
references on "special file descriptions" (FDs for files such as pipes,
sockets, and devices), which use per-FD handles (fids) instead of
dentry-shared handles, for sync() and syncfs().
PiperOrigin-RevId: 282473364
2019-11-26 02:09:15 +00:00
|
|
|
// GetFilesystem implements vfs.FilesystemType.GetFilesystem.
|
2020-05-14 16:34:21 +00:00
|
|
|
func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *auth.Credentials, _ string, opts vfs.GetFilesystemOptions) (*vfs.Filesystem, *vfs.Dentry, error) {
|
2020-01-06 20:51:35 +00:00
|
|
|
memFileProvider := pgalloc.MemoryFileProviderFromContext(ctx)
|
|
|
|
if memFileProvider == nil {
|
|
|
|
panic("MemoryFileProviderFromContext returned nil")
|
|
|
|
}
|
2020-03-31 22:00:25 +00:00
|
|
|
|
2020-04-01 19:05:17 +00:00
|
|
|
rootFileType := uint16(linux.S_IFDIR)
|
|
|
|
newFSType := vfs.FilesystemType(&fstype)
|
2020-03-31 22:00:25 +00:00
|
|
|
tmpfsOpts, ok := opts.InternalData.(FilesystemOpts)
|
2020-04-01 19:05:17 +00:00
|
|
|
if ok {
|
|
|
|
if tmpfsOpts.RootFileType != 0 {
|
|
|
|
rootFileType = tmpfsOpts.RootFileType
|
|
|
|
}
|
|
|
|
if tmpfsOpts.FilesystemType != nil {
|
|
|
|
newFSType = tmpfsOpts.FilesystemType
|
|
|
|
}
|
2020-03-31 22:00:25 +00:00
|
|
|
}
|
2020-04-01 19:05:17 +00:00
|
|
|
|
2020-06-06 02:10:28 +00:00
|
|
|
mopts := vfs.GenericParseMountOptions(opts.Data)
|
2020-06-08 20:27:06 +00:00
|
|
|
rootMode := linux.FileMode(0777)
|
|
|
|
if rootFileType == linux.S_IFDIR {
|
|
|
|
rootMode = 01777
|
|
|
|
}
|
|
|
|
modeStr, ok := mopts["mode"]
|
|
|
|
if ok {
|
|
|
|
delete(mopts, "mode")
|
2020-06-06 02:10:28 +00:00
|
|
|
mode, err := strconv.ParseUint(modeStr, 8, 32)
|
|
|
|
if err != nil {
|
2020-06-08 20:27:06 +00:00
|
|
|
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: invalid mode: %q", modeStr)
|
|
|
|
return nil, nil, syserror.EINVAL
|
2020-06-06 02:10:28 +00:00
|
|
|
}
|
2020-06-08 20:27:06 +00:00
|
|
|
rootMode = linux.FileMode(mode & 07777)
|
2020-06-06 02:10:28 +00:00
|
|
|
}
|
2020-06-08 20:27:06 +00:00
|
|
|
rootKUID := creds.EffectiveKUID
|
|
|
|
uidStr, ok := mopts["uid"]
|
|
|
|
if ok {
|
|
|
|
delete(mopts, "uid")
|
|
|
|
uid, err := strconv.ParseUint(uidStr, 10, 32)
|
2020-06-06 02:10:28 +00:00
|
|
|
if err != nil {
|
2020-06-08 20:27:06 +00:00
|
|
|
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: invalid uid: %q", uidStr)
|
|
|
|
return nil, nil, syserror.EINVAL
|
2020-06-06 02:10:28 +00:00
|
|
|
}
|
2020-06-08 20:27:06 +00:00
|
|
|
kuid := creds.UserNamespace.MapToKUID(auth.UID(uid))
|
|
|
|
if !kuid.Ok() {
|
|
|
|
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: unmapped uid: %d", uid)
|
|
|
|
return nil, nil, syserror.EINVAL
|
2020-06-06 02:10:28 +00:00
|
|
|
}
|
2020-06-08 20:27:06 +00:00
|
|
|
rootKUID = kuid
|
2020-06-06 02:10:28 +00:00
|
|
|
}
|
2020-06-08 20:27:06 +00:00
|
|
|
rootKGID := creds.EffectiveKGID
|
|
|
|
gidStr, ok := mopts["gid"]
|
|
|
|
if ok {
|
|
|
|
delete(mopts, "gid")
|
|
|
|
gid, err := strconv.ParseUint(gidStr, 10, 32)
|
2020-06-06 02:10:28 +00:00
|
|
|
if err != nil {
|
2020-06-08 20:27:06 +00:00
|
|
|
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: invalid gid: %q", gidStr)
|
|
|
|
return nil, nil, syserror.EINVAL
|
2020-06-06 02:10:28 +00:00
|
|
|
}
|
2020-06-08 20:27:06 +00:00
|
|
|
kgid := creds.UserNamespace.MapToKGID(auth.GID(gid))
|
|
|
|
if !kgid.Ok() {
|
|
|
|
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: unmapped gid: %d", gid)
|
|
|
|
return nil, nil, syserror.EINVAL
|
2020-06-06 02:10:28 +00:00
|
|
|
}
|
2020-06-08 20:27:06 +00:00
|
|
|
rootKGID = kgid
|
|
|
|
}
|
|
|
|
if len(mopts) != 0 {
|
|
|
|
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: unknown options: %v", mopts)
|
|
|
|
return nil, nil, syserror.EINVAL
|
|
|
|
}
|
|
|
|
|
|
|
|
devMinor, err := vfsObj.GetAnonBlockDevMinor()
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
2020-06-06 02:10:28 +00:00
|
|
|
}
|
2020-06-08 20:27:06 +00:00
|
|
|
clock := time.RealtimeClockFromContext(ctx)
|
|
|
|
fs := filesystem{
|
|
|
|
memFile: memFileProvider.MemoryFile(),
|
|
|
|
clock: clock,
|
|
|
|
devMinor: devMinor,
|
|
|
|
}
|
|
|
|
fs.vfsfs.Init(vfsObj, newFSType, &fs)
|
2020-06-06 02:10:28 +00:00
|
|
|
|
2020-04-21 19:16:42 +00:00
|
|
|
var root *dentry
|
2020-04-01 19:05:17 +00:00
|
|
|
switch rootFileType {
|
2020-03-31 22:00:25 +00:00
|
|
|
case linux.S_IFREG:
|
2020-06-08 20:27:06 +00:00
|
|
|
root = fs.newDentry(fs.newRegularFile(rootKUID, rootKGID, rootMode))
|
2020-03-31 22:00:25 +00:00
|
|
|
case linux.S_IFLNK:
|
2020-06-08 20:27:06 +00:00
|
|
|
root = fs.newDentry(fs.newSymlink(rootKUID, rootKGID, rootMode, tmpfsOpts.RootSymlinkTarget))
|
2020-03-31 22:00:25 +00:00
|
|
|
case linux.S_IFDIR:
|
2020-06-08 20:27:06 +00:00
|
|
|
root = &fs.newDirectory(rootKUID, rootKGID, rootMode).dentry
|
2020-03-31 22:00:25 +00:00
|
|
|
default:
|
2020-05-07 21:00:36 +00:00
|
|
|
fs.vfsfs.DecRef()
|
2020-04-01 19:05:17 +00:00
|
|
|
return nil, nil, fmt.Errorf("invalid tmpfs root file type: %#o", rootFileType)
|
2020-03-31 22:00:25 +00:00
|
|
|
}
|
2020-04-21 19:16:42 +00:00
|
|
|
return &fs.vfsfs, &root.vfsd, nil
|
2019-07-18 22:09:14 +00:00
|
|
|
}
|
|
|
|
|
2020-05-14 16:34:21 +00:00
|
|
|
// NewFilesystem returns a new tmpfs filesystem.
|
|
|
|
func NewFilesystem(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *auth.Credentials) (*vfs.Filesystem, *vfs.Dentry, error) {
|
|
|
|
return FilesystemType{}.GetFilesystem(ctx, vfsObj, creds, "", vfs.GetFilesystemOptions{})
|
|
|
|
}
|
|
|
|
|
2019-07-18 22:09:14 +00:00
|
|
|
// Release implements vfs.FilesystemImpl.Release.
|
2019-08-08 18:45:33 +00:00
|
|
|
func (fs *filesystem) Release() {
|
2020-05-07 21:00:36 +00:00
|
|
|
fs.vfsfs.VirtualFilesystem().PutAnonBlockDevMinor(fs.devMinor)
|
2019-07-18 22:09:14 +00:00
|
|
|
}
|
|
|
|
|
2019-08-08 18:45:33 +00:00
|
|
|
// dentry implements vfs.DentryImpl.
|
|
|
|
type dentry struct {
|
2019-07-18 22:09:14 +00:00
|
|
|
vfsd vfs.Dentry
|
|
|
|
|
2020-04-21 19:16:42 +00:00
|
|
|
// parent is this dentry's parent directory. Each referenced dentry holds a
|
|
|
|
// reference on parent.dentry. If this dentry is a filesystem root, parent
|
|
|
|
// is nil. parent is protected by filesystem.mu.
|
|
|
|
parent *dentry
|
|
|
|
|
|
|
|
// name is the name of this dentry in its parent. If this dentry is a
|
|
|
|
// filesystem root, name is the empty string. name is protected by
|
|
|
|
// filesystem.mu.
|
|
|
|
name string
|
|
|
|
|
2020-05-29 19:27:15 +00:00
|
|
|
// unlinked indicates whether this dentry has been unlinked from its parent.
|
|
|
|
// It is only set to true on an unlink operation, and never set from true to
|
|
|
|
// false. unlinked is protected by filesystem.mu.
|
|
|
|
unlinked bool
|
|
|
|
|
2020-04-21 19:16:42 +00:00
|
|
|
// dentryEntry (ugh) links dentries into their parent directory.childList.
|
|
|
|
dentryEntry
|
|
|
|
|
2019-08-08 18:45:33 +00:00
|
|
|
// inode is the inode represented by this dentry. Multiple Dentries may
|
|
|
|
// share a single non-directory inode (with hard links). inode is
|
2019-07-18 22:09:14 +00:00
|
|
|
// immutable.
|
2020-04-21 19:16:42 +00:00
|
|
|
//
|
2020-01-06 20:51:35 +00:00
|
|
|
// tmpfs doesn't count references on dentries; because the dentry tree is
|
2019-07-18 22:09:14 +00:00
|
|
|
// the sole source of truth, it is by definition always consistent with the
|
2019-08-08 18:45:33 +00:00
|
|
|
// state of the filesystem. However, it does count references on inodes,
|
|
|
|
// because inode resources are released when all references are dropped.
|
2020-04-21 19:16:42 +00:00
|
|
|
// dentry therefore forwards reference counting directly to inode.
|
|
|
|
inode *inode
|
2019-07-18 22:09:14 +00:00
|
|
|
}
|
|
|
|
|
2019-08-08 18:45:33 +00:00
|
|
|
func (fs *filesystem) newDentry(inode *inode) *dentry {
|
|
|
|
d := &dentry{
|
2019-07-18 22:09:14 +00:00
|
|
|
inode: inode,
|
|
|
|
}
|
|
|
|
d.vfsd.Init(d)
|
|
|
|
return d
|
|
|
|
}
|
|
|
|
|
|
|
|
// IncRef implements vfs.DentryImpl.IncRef.
|
Minor VFS2 interface changes.
- Remove the Filesystem argument from DentryImpl.*Ref(); in general DentryImpls
that need the Filesystem for reference counting will probably also need it
for other interface methods that don't plumb Filesystem, so it's easier to
just store a pointer to the filesystem in the DentryImpl.
- Add a pointer to the VirtualFilesystem to Filesystem, which is needed by the
gofer client to disown dentries for cache eviction triggered by dentry
reference count changes.
- Rename FilesystemType.NewFilesystem to GetFilesystem; in some cases (e.g.
sysfs, cgroupfs) it's much cleaner for there to be only one Filesystem that
is used by all mounts, and in at least one case (devtmpfs) it's visibly
incorrect not to do so, so NewFilesystem doesn't always actually create and
return a *new* Filesystem.
- Require callers of FileDescription.Init() to increment Mount/Dentry
references. This is because the gofer client may, in the OpenAt() path, take
a reference on a dentry with 0 references, which is safe due to
synchronization that is outside the scope of this CL, and it would be safer
to still have its implementation of DentryImpl.IncRef() check for an
increment for 0 references in other cases.
- Add FileDescription.TryIncRef. This is used by the gofer client to take
references on "special file descriptions" (FDs for files such as pipes,
sockets, and devices), which use per-FD handles (fids) instead of
dentry-shared handles, for sync() and syncfs().
PiperOrigin-RevId: 282473364
2019-11-26 02:09:15 +00:00
|
|
|
func (d *dentry) IncRef() {
|
2019-07-18 22:09:14 +00:00
|
|
|
d.inode.incRef()
|
|
|
|
}
|
|
|
|
|
|
|
|
// TryIncRef implements vfs.DentryImpl.TryIncRef.
|
Minor VFS2 interface changes.
- Remove the Filesystem argument from DentryImpl.*Ref(); in general DentryImpls
that need the Filesystem for reference counting will probably also need it
for other interface methods that don't plumb Filesystem, so it's easier to
just store a pointer to the filesystem in the DentryImpl.
- Add a pointer to the VirtualFilesystem to Filesystem, which is needed by the
gofer client to disown dentries for cache eviction triggered by dentry
reference count changes.
- Rename FilesystemType.NewFilesystem to GetFilesystem; in some cases (e.g.
sysfs, cgroupfs) it's much cleaner for there to be only one Filesystem that
is used by all mounts, and in at least one case (devtmpfs) it's visibly
incorrect not to do so, so NewFilesystem doesn't always actually create and
return a *new* Filesystem.
- Require callers of FileDescription.Init() to increment Mount/Dentry
references. This is because the gofer client may, in the OpenAt() path, take
a reference on a dentry with 0 references, which is safe due to
synchronization that is outside the scope of this CL, and it would be safer
to still have its implementation of DentryImpl.IncRef() check for an
increment for 0 references in other cases.
- Add FileDescription.TryIncRef. This is used by the gofer client to take
references on "special file descriptions" (FDs for files such as pipes,
sockets, and devices), which use per-FD handles (fids) instead of
dentry-shared handles, for sync() and syncfs().
PiperOrigin-RevId: 282473364
2019-11-26 02:09:15 +00:00
|
|
|
func (d *dentry) TryIncRef() bool {
|
2019-07-18 22:09:14 +00:00
|
|
|
return d.inode.tryIncRef()
|
|
|
|
}
|
|
|
|
|
|
|
|
// DecRef implements vfs.DentryImpl.DecRef.
|
Minor VFS2 interface changes.
- Remove the Filesystem argument from DentryImpl.*Ref(); in general DentryImpls
that need the Filesystem for reference counting will probably also need it
for other interface methods that don't plumb Filesystem, so it's easier to
just store a pointer to the filesystem in the DentryImpl.
- Add a pointer to the VirtualFilesystem to Filesystem, which is needed by the
gofer client to disown dentries for cache eviction triggered by dentry
reference count changes.
- Rename FilesystemType.NewFilesystem to GetFilesystem; in some cases (e.g.
sysfs, cgroupfs) it's much cleaner for there to be only one Filesystem that
is used by all mounts, and in at least one case (devtmpfs) it's visibly
incorrect not to do so, so NewFilesystem doesn't always actually create and
return a *new* Filesystem.
- Require callers of FileDescription.Init() to increment Mount/Dentry
references. This is because the gofer client may, in the OpenAt() path, take
a reference on a dentry with 0 references, which is safe due to
synchronization that is outside the scope of this CL, and it would be safer
to still have its implementation of DentryImpl.IncRef() check for an
increment for 0 references in other cases.
- Add FileDescription.TryIncRef. This is used by the gofer client to take
references on "special file descriptions" (FDs for files such as pipes,
sockets, and devices), which use per-FD handles (fids) instead of
dentry-shared handles, for sync() and syncfs().
PiperOrigin-RevId: 282473364
2019-11-26 02:09:15 +00:00
|
|
|
func (d *dentry) DecRef() {
|
2019-07-18 22:09:14 +00:00
|
|
|
d.inode.decRef()
|
|
|
|
}
|
|
|
|
|
2020-05-29 15:07:44 +00:00
|
|
|
// InotifyWithParent implements vfs.DentryImpl.InotifyWithParent.
|
2020-05-29 19:27:15 +00:00
|
|
|
func (d *dentry) InotifyWithParent(events uint32, cookie uint32, et vfs.EventType) {
|
2020-05-29 15:07:44 +00:00
|
|
|
if d.inode.isDir() {
|
|
|
|
events |= linux.IN_ISDIR
|
|
|
|
}
|
|
|
|
|
|
|
|
// The ordering below is important, Linux always notifies the parent first.
|
|
|
|
if d.parent != nil {
|
|
|
|
// Note that d.parent or d.name may be stale if there is a concurrent
|
|
|
|
// rename operation. Inotify does not provide consistency guarantees.
|
2020-05-29 19:27:15 +00:00
|
|
|
d.parent.inode.watches.NotifyWithExclusions(d.name, events, cookie, et, d.unlinked)
|
2020-05-29 15:07:44 +00:00
|
|
|
}
|
2020-05-29 19:27:15 +00:00
|
|
|
d.inode.watches.Notify("", events, cookie, et)
|
2020-05-29 15:07:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Watches implements vfs.DentryImpl.Watches.
|
|
|
|
func (d *dentry) Watches() *vfs.Watches {
|
|
|
|
return &d.inode.watches
|
|
|
|
}
|
|
|
|
|
2019-08-08 18:45:33 +00:00
|
|
|
// inode represents a filesystem object.
|
|
|
|
type inode struct {
|
2020-05-07 21:00:36 +00:00
|
|
|
// fs is the owning filesystem. fs is immutable.
|
|
|
|
fs *filesystem
|
2020-01-16 21:58:25 +00:00
|
|
|
|
2019-07-18 22:09:14 +00:00
|
|
|
// refs is a reference count. refs is accessed using atomic memory
|
|
|
|
// operations.
|
|
|
|
//
|
2020-05-28 01:18:17 +00:00
|
|
|
// A reference is held on all inodes as long as they are reachable in the
|
|
|
|
// filesystem tree, i.e. nlink is nonzero. This reference is dropped when
|
|
|
|
// nlink reaches 0.
|
2019-07-18 22:09:14 +00:00
|
|
|
refs int64
|
|
|
|
|
2020-04-11 02:01:39 +00:00
|
|
|
// xattrs implements extended attributes.
|
|
|
|
//
|
|
|
|
// TODO(b/148380782): Support xattrs other than user.*
|
|
|
|
xattrs memxattr.SimpleExtendedAttributes
|
|
|
|
|
2020-01-16 00:31:24 +00:00
|
|
|
// Inode metadata. Writing multiple fields atomically requires holding
|
|
|
|
// mu, othewise atomic operations can be used.
|
|
|
|
mu sync.Mutex
|
2020-03-25 21:44:18 +00:00
|
|
|
mode uint32 // file type and mode
|
2019-08-08 18:45:33 +00:00
|
|
|
nlink uint32 // protected by filesystem.mu instead of inode.mu
|
2019-07-18 22:09:14 +00:00
|
|
|
uid uint32 // auth.KUID, but stored as raw uint32 for sync/atomic
|
|
|
|
gid uint32 // auth.KGID, but ...
|
|
|
|
ino uint64 // immutable
|
|
|
|
|
2020-01-16 00:31:24 +00:00
|
|
|
// Linux's tmpfs has no concept of btime.
|
|
|
|
atime int64 // nanoseconds
|
|
|
|
ctime int64 // nanoseconds
|
|
|
|
mtime int64 // nanoseconds
|
|
|
|
|
2020-01-31 22:14:52 +00:00
|
|
|
locks lock.FileLocks
|
|
|
|
|
2020-05-29 15:07:44 +00:00
|
|
|
// Inotify watches for this inode.
|
|
|
|
watches vfs.Watches
|
|
|
|
|
2019-07-18 22:09:14 +00:00
|
|
|
impl interface{} // immutable
|
|
|
|
}
|
|
|
|
|
2019-12-23 21:17:29 +00:00
|
|
|
const maxLinks = math.MaxUint32
|
|
|
|
|
2020-06-08 20:27:06 +00:00
|
|
|
func (i *inode) init(impl interface{}, fs *filesystem, kuid auth.KUID, kgid auth.KGID, mode linux.FileMode) {
|
2020-03-25 21:44:18 +00:00
|
|
|
if mode.FileType() == 0 {
|
|
|
|
panic("file type is required in FileMode")
|
|
|
|
}
|
2020-05-07 21:00:36 +00:00
|
|
|
i.fs = fs
|
2019-07-18 22:09:14 +00:00
|
|
|
i.refs = 1
|
|
|
|
i.mode = uint32(mode)
|
2020-06-08 20:27:06 +00:00
|
|
|
i.uid = uint32(kuid)
|
|
|
|
i.gid = uint32(kgid)
|
2019-07-18 22:09:14 +00:00
|
|
|
i.ino = atomic.AddUint64(&fs.nextInoMinusOne, 1)
|
2020-01-16 00:31:24 +00:00
|
|
|
// Tmpfs creation sets atime, ctime, and mtime to current time.
|
2020-04-21 19:16:42 +00:00
|
|
|
now := fs.clock.Now().Nanoseconds()
|
2020-01-16 00:31:24 +00:00
|
|
|
i.atime = now
|
|
|
|
i.ctime = now
|
|
|
|
i.mtime = now
|
2019-07-18 22:09:14 +00:00
|
|
|
// i.nlink initialized by caller
|
2020-05-29 15:07:44 +00:00
|
|
|
i.watches = vfs.Watches{}
|
2019-07-18 22:09:14 +00:00
|
|
|
i.impl = impl
|
|
|
|
}
|
|
|
|
|
2019-12-23 21:17:29 +00:00
|
|
|
// incLinksLocked increments i's link count.
|
|
|
|
//
|
|
|
|
// Preconditions: filesystem.mu must be locked for writing. i.nlink != 0.
|
|
|
|
// i.nlink < maxLinks.
|
2019-08-08 18:45:33 +00:00
|
|
|
func (i *inode) incLinksLocked() {
|
2019-12-23 21:17:29 +00:00
|
|
|
if i.nlink == 0 {
|
2020-01-06 20:51:35 +00:00
|
|
|
panic("tmpfs.inode.incLinksLocked() called with no existing links")
|
2019-07-18 22:09:14 +00:00
|
|
|
}
|
2019-12-23 21:17:29 +00:00
|
|
|
if i.nlink == maxLinks {
|
2020-04-20 17:09:23 +00:00
|
|
|
panic("tmpfs.inode.incLinksLocked() called with maximum link count")
|
2019-12-23 21:17:29 +00:00
|
|
|
}
|
|
|
|
atomic.AddUint32(&i.nlink, 1)
|
2019-07-18 22:09:14 +00:00
|
|
|
}
|
|
|
|
|
2020-05-28 01:18:17 +00:00
|
|
|
// decLinksLocked decrements i's link count. If the link count reaches 0, we
|
|
|
|
// remove a reference on i as well.
|
2019-12-23 21:17:29 +00:00
|
|
|
//
|
|
|
|
// Preconditions: filesystem.mu must be locked for writing. i.nlink != 0.
|
2019-08-08 18:45:33 +00:00
|
|
|
func (i *inode) decLinksLocked() {
|
2019-12-23 21:17:29 +00:00
|
|
|
if i.nlink == 0 {
|
2020-01-06 20:51:35 +00:00
|
|
|
panic("tmpfs.inode.decLinksLocked() called with no existing links")
|
2019-07-18 22:09:14 +00:00
|
|
|
}
|
2020-05-28 01:18:17 +00:00
|
|
|
if atomic.AddUint32(&i.nlink, ^uint32(0)) == 0 {
|
|
|
|
i.decRef()
|
|
|
|
}
|
2019-07-18 22:09:14 +00:00
|
|
|
}
|
|
|
|
|
2019-08-08 18:45:33 +00:00
|
|
|
func (i *inode) incRef() {
|
2019-07-18 22:09:14 +00:00
|
|
|
if atomic.AddInt64(&i.refs, 1) <= 1 {
|
2020-01-06 20:51:35 +00:00
|
|
|
panic("tmpfs.inode.incRef() called without holding a reference")
|
2019-07-18 22:09:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-08 18:45:33 +00:00
|
|
|
func (i *inode) tryIncRef() bool {
|
2019-07-18 22:09:14 +00:00
|
|
|
for {
|
|
|
|
refs := atomic.LoadInt64(&i.refs)
|
|
|
|
if refs == 0 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if atomic.CompareAndSwapInt64(&i.refs, refs, refs+1) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-08 18:45:33 +00:00
|
|
|
func (i *inode) decRef() {
|
2019-07-18 22:09:14 +00:00
|
|
|
if refs := atomic.AddInt64(&i.refs, -1); refs == 0 {
|
2020-05-29 15:07:44 +00:00
|
|
|
i.watches.HandleDeletion()
|
2020-01-06 20:51:35 +00:00
|
|
|
if regFile, ok := i.impl.(*regularFile); ok {
|
2020-04-21 19:16:42 +00:00
|
|
|
// Release memory used by regFile to store data. Since regFile is
|
|
|
|
// no longer usable, we don't need to grab any locks or update any
|
|
|
|
// metadata.
|
2020-01-06 20:51:35 +00:00
|
|
|
regFile.data.DropAll(regFile.memFile)
|
2019-07-18 22:09:14 +00:00
|
|
|
}
|
|
|
|
} else if refs < 0 {
|
2020-01-06 20:51:35 +00:00
|
|
|
panic("tmpfs.inode.decRef() called without holding a reference")
|
2019-07-18 22:09:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-26 02:12:25 +00:00
|
|
|
func (i *inode) checkPermissions(creds *auth.Credentials, ats vfs.AccessTypes) error {
|
|
|
|
mode := linux.FileMode(atomic.LoadUint32(&i.mode))
|
|
|
|
return vfs.GenericCheckPermissions(creds, ats, mode, auth.KUID(atomic.LoadUint32(&i.uid)), auth.KGID(atomic.LoadUint32(&i.gid)))
|
2019-07-18 22:09:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Go won't inline this function, and returning linux.Statx (which is quite
|
|
|
|
// big) means spending a lot of time in runtime.duffcopy(), so instead it's an
|
|
|
|
// output parameter.
|
2020-01-16 00:31:24 +00:00
|
|
|
//
|
|
|
|
// Note that Linux does not guarantee to return consistent data (in the case of
|
|
|
|
// a concurrent modification), so we do not require holding inode.mu.
|
2019-08-08 18:45:33 +00:00
|
|
|
func (i *inode) statTo(stat *linux.Statx) {
|
2020-01-16 00:31:24 +00:00
|
|
|
stat.Mask = linux.STATX_TYPE | linux.STATX_MODE | linux.STATX_NLINK |
|
2020-04-21 19:16:42 +00:00
|
|
|
linux.STATX_UID | linux.STATX_GID | linux.STATX_INO | linux.STATX_SIZE |
|
|
|
|
linux.STATX_BLOCKS | linux.STATX_ATIME | linux.STATX_CTIME |
|
|
|
|
linux.STATX_MTIME
|
|
|
|
stat.Blksize = usermem.PageSize
|
2019-07-18 22:09:14 +00:00
|
|
|
stat.Nlink = atomic.LoadUint32(&i.nlink)
|
|
|
|
stat.UID = atomic.LoadUint32(&i.uid)
|
|
|
|
stat.GID = atomic.LoadUint32(&i.gid)
|
|
|
|
stat.Mode = uint16(atomic.LoadUint32(&i.mode))
|
|
|
|
stat.Ino = i.ino
|
2020-01-16 00:31:24 +00:00
|
|
|
stat.Atime = linux.NsecToStatxTimestamp(i.atime)
|
|
|
|
stat.Ctime = linux.NsecToStatxTimestamp(i.ctime)
|
|
|
|
stat.Mtime = linux.NsecToStatxTimestamp(i.mtime)
|
2020-05-07 21:00:36 +00:00
|
|
|
stat.DevMajor = linux.UNNAMED_MAJOR
|
|
|
|
stat.DevMinor = i.fs.devMinor
|
2019-07-18 22:09:14 +00:00
|
|
|
switch impl := i.impl.(type) {
|
|
|
|
case *regularFile:
|
|
|
|
stat.Mask |= linux.STATX_SIZE | linux.STATX_BLOCKS
|
2020-01-06 20:51:35 +00:00
|
|
|
stat.Size = uint64(atomic.LoadUint64(&impl.size))
|
2020-04-21 19:16:42 +00:00
|
|
|
// TODO(jamieliu): This should be impl.data.Span() / 512, but this is
|
|
|
|
// too expensive to compute here. Cache it in regularFile.
|
2019-07-18 22:09:14 +00:00
|
|
|
stat.Blocks = allocatedBlocksForSize(stat.Size)
|
2020-04-21 19:16:42 +00:00
|
|
|
case *directory:
|
|
|
|
// "20" is mm/shmem.c:BOGO_DIRENT_SIZE.
|
|
|
|
stat.Size = 20 * (2 + uint64(atomic.LoadInt64(&impl.numChildren)))
|
|
|
|
// stat.Blocks is 0.
|
2019-07-18 22:09:14 +00:00
|
|
|
case *symlink:
|
|
|
|
stat.Size = uint64(len(impl.target))
|
2020-04-21 19:16:42 +00:00
|
|
|
// stat.Blocks is 0.
|
|
|
|
case *namedPipe, *socketFile:
|
|
|
|
// stat.Size and stat.Blocks are 0.
|
2020-01-25 01:06:30 +00:00
|
|
|
case *deviceFile:
|
2020-04-21 19:16:42 +00:00
|
|
|
// stat.Size and stat.Blocks are 0.
|
2020-01-25 01:06:30 +00:00
|
|
|
stat.RdevMajor = impl.major
|
|
|
|
stat.RdevMinor = impl.minor
|
2019-07-18 22:09:14 +00:00
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf("unknown inode type: %T", i.impl))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-16 22:59:29 +00:00
|
|
|
func (i *inode) setStat(ctx context.Context, creds *auth.Credentials, stat *linux.Statx) error {
|
2020-01-16 00:31:24 +00:00
|
|
|
if stat.Mask == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
2020-03-16 20:28:00 +00:00
|
|
|
if stat.Mask&^(linux.STATX_MODE|linux.STATX_UID|linux.STATX_GID|linux.STATX_ATIME|linux.STATX_MTIME|linux.STATX_CTIME|linux.STATX_SIZE) != 0 {
|
|
|
|
return syserror.EPERM
|
|
|
|
}
|
2020-03-26 02:12:25 +00:00
|
|
|
mode := linux.FileMode(atomic.LoadUint32(&i.mode))
|
|
|
|
if err := vfs.CheckSetStat(ctx, creds, stat, mode, auth.KUID(atomic.LoadUint32(&i.uid)), auth.KGID(atomic.LoadUint32(&i.gid))); err != nil {
|
2020-03-16 20:28:00 +00:00
|
|
|
return err
|
|
|
|
}
|
2020-01-16 00:31:24 +00:00
|
|
|
i.mu.Lock()
|
2020-04-17 02:26:02 +00:00
|
|
|
defer i.mu.Unlock()
|
2020-01-16 21:58:25 +00:00
|
|
|
var (
|
|
|
|
needsMtimeBump bool
|
|
|
|
needsCtimeBump bool
|
|
|
|
)
|
2020-01-16 00:31:24 +00:00
|
|
|
mask := stat.Mask
|
|
|
|
if mask&linux.STATX_MODE != 0 {
|
2020-03-25 21:44:18 +00:00
|
|
|
ft := atomic.LoadUint32(&i.mode) & linux.S_IFMT
|
|
|
|
atomic.StoreUint32(&i.mode, ft|uint32(stat.Mode&^linux.S_IFMT))
|
2020-01-16 21:58:25 +00:00
|
|
|
needsCtimeBump = true
|
2020-01-16 00:31:24 +00:00
|
|
|
}
|
|
|
|
if mask&linux.STATX_UID != 0 {
|
|
|
|
atomic.StoreUint32(&i.uid, stat.UID)
|
2020-01-16 21:58:25 +00:00
|
|
|
needsCtimeBump = true
|
2020-01-16 00:31:24 +00:00
|
|
|
}
|
|
|
|
if mask&linux.STATX_GID != 0 {
|
|
|
|
atomic.StoreUint32(&i.gid, stat.GID)
|
2020-01-16 21:58:25 +00:00
|
|
|
needsCtimeBump = true
|
|
|
|
}
|
|
|
|
if mask&linux.STATX_SIZE != 0 {
|
|
|
|
switch impl := i.impl.(type) {
|
|
|
|
case *regularFile:
|
2020-02-20 17:57:06 +00:00
|
|
|
updated, err := impl.truncateLocked(stat.Size)
|
2020-01-16 21:58:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if updated {
|
|
|
|
needsMtimeBump = true
|
|
|
|
needsCtimeBump = true
|
|
|
|
}
|
|
|
|
case *directory:
|
|
|
|
return syserror.EISDIR
|
|
|
|
default:
|
2020-01-25 01:06:30 +00:00
|
|
|
return syserror.EINVAL
|
2020-01-16 21:58:25 +00:00
|
|
|
}
|
2020-01-16 00:31:24 +00:00
|
|
|
}
|
2020-05-07 21:00:36 +00:00
|
|
|
now := i.fs.clock.Now().Nanoseconds()
|
2020-01-16 00:31:24 +00:00
|
|
|
if mask&linux.STATX_ATIME != 0 {
|
2020-04-03 02:37:41 +00:00
|
|
|
if stat.Atime.Nsec == linux.UTIME_NOW {
|
|
|
|
atomic.StoreInt64(&i.atime, now)
|
|
|
|
} else {
|
|
|
|
atomic.StoreInt64(&i.atime, stat.Atime.ToNsecCapped())
|
|
|
|
}
|
2020-01-16 21:58:25 +00:00
|
|
|
needsCtimeBump = true
|
|
|
|
}
|
|
|
|
if mask&linux.STATX_MTIME != 0 {
|
2020-04-03 02:37:41 +00:00
|
|
|
if stat.Mtime.Nsec == linux.UTIME_NOW {
|
|
|
|
atomic.StoreInt64(&i.mtime, now)
|
|
|
|
} else {
|
|
|
|
atomic.StoreInt64(&i.mtime, stat.Mtime.ToNsecCapped())
|
|
|
|
}
|
2020-01-16 21:58:25 +00:00
|
|
|
needsCtimeBump = true
|
|
|
|
// Ignore the mtime bump, since we just set it ourselves.
|
|
|
|
needsMtimeBump = false
|
2020-01-16 00:31:24 +00:00
|
|
|
}
|
|
|
|
if mask&linux.STATX_CTIME != 0 {
|
2020-04-03 02:37:41 +00:00
|
|
|
if stat.Ctime.Nsec == linux.UTIME_NOW {
|
|
|
|
atomic.StoreInt64(&i.ctime, now)
|
|
|
|
} else {
|
|
|
|
atomic.StoreInt64(&i.ctime, stat.Ctime.ToNsecCapped())
|
|
|
|
}
|
2020-01-16 21:58:25 +00:00
|
|
|
// Ignore the ctime bump, since we just set it ourselves.
|
|
|
|
needsCtimeBump = false
|
2020-01-16 00:31:24 +00:00
|
|
|
}
|
2020-01-16 21:58:25 +00:00
|
|
|
if needsMtimeBump {
|
|
|
|
atomic.StoreInt64(&i.mtime, now)
|
|
|
|
}
|
|
|
|
if needsCtimeBump {
|
|
|
|
atomic.StoreInt64(&i.ctime, now)
|
2020-01-16 00:31:24 +00:00
|
|
|
}
|
2020-04-03 02:37:41 +00:00
|
|
|
|
2020-01-16 00:31:24 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-07-18 22:09:14 +00:00
|
|
|
// allocatedBlocksForSize returns the number of 512B blocks needed to
|
|
|
|
// accommodate the given size in bytes, as appropriate for struct
|
|
|
|
// stat::st_blocks and struct statx::stx_blocks. (Note that this 512B block
|
|
|
|
// size is independent of the "preferred block size for I/O", struct
|
|
|
|
// stat::st_blksize and struct statx::stx_blksize.)
|
|
|
|
func allocatedBlocksForSize(size uint64) uint64 {
|
|
|
|
return (size + 511) / 512
|
|
|
|
}
|
|
|
|
|
2019-08-08 18:45:33 +00:00
|
|
|
func (i *inode) direntType() uint8 {
|
2020-01-25 01:06:30 +00:00
|
|
|
switch impl := i.impl.(type) {
|
2019-07-18 22:09:14 +00:00
|
|
|
case *regularFile:
|
|
|
|
return linux.DT_REG
|
|
|
|
case *directory:
|
|
|
|
return linux.DT_DIR
|
|
|
|
case *symlink:
|
|
|
|
return linux.DT_LNK
|
2020-04-06 14:30:20 +00:00
|
|
|
case *socketFile:
|
|
|
|
return linux.DT_SOCK
|
2020-01-25 01:06:30 +00:00
|
|
|
case *deviceFile:
|
|
|
|
switch impl.kind {
|
|
|
|
case vfs.BlockDevice:
|
|
|
|
return linux.DT_BLK
|
|
|
|
case vfs.CharDevice:
|
|
|
|
return linux.DT_CHR
|
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf("unknown vfs.DeviceKind: %v", impl.kind))
|
|
|
|
}
|
2019-07-18 22:09:14 +00:00
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf("unknown inode type: %T", i.impl))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-25 21:44:18 +00:00
|
|
|
func (i *inode) isDir() bool {
|
|
|
|
return linux.FileMode(i.mode).FileType() == linux.S_IFDIR
|
|
|
|
}
|
|
|
|
|
2020-04-03 02:37:41 +00:00
|
|
|
func (i *inode) touchAtime(mnt *vfs.Mount) {
|
2020-06-06 02:10:28 +00:00
|
|
|
if mnt.Flags.NoATime {
|
|
|
|
return
|
|
|
|
}
|
2020-04-03 02:37:41 +00:00
|
|
|
if err := mnt.CheckBeginWrite(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
2020-05-07 21:00:36 +00:00
|
|
|
now := i.fs.clock.Now().Nanoseconds()
|
2020-04-03 02:37:41 +00:00
|
|
|
i.mu.Lock()
|
|
|
|
atomic.StoreInt64(&i.atime, now)
|
|
|
|
i.mu.Unlock()
|
|
|
|
mnt.EndWrite()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Preconditions: The caller has called vfs.Mount.CheckBeginWrite().
|
|
|
|
func (i *inode) touchCtime() {
|
2020-05-07 21:00:36 +00:00
|
|
|
now := i.fs.clock.Now().Nanoseconds()
|
2020-04-03 02:37:41 +00:00
|
|
|
i.mu.Lock()
|
|
|
|
atomic.StoreInt64(&i.ctime, now)
|
|
|
|
i.mu.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Preconditions: The caller has called vfs.Mount.CheckBeginWrite().
|
|
|
|
func (i *inode) touchCMtime() {
|
2020-05-07 21:00:36 +00:00
|
|
|
now := i.fs.clock.Now().Nanoseconds()
|
2020-04-03 02:37:41 +00:00
|
|
|
i.mu.Lock()
|
|
|
|
atomic.StoreInt64(&i.mtime, now)
|
|
|
|
atomic.StoreInt64(&i.ctime, now)
|
|
|
|
i.mu.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Preconditions: The caller has called vfs.Mount.CheckBeginWrite() and holds
|
|
|
|
// inode.mu.
|
|
|
|
func (i *inode) touchCMtimeLocked() {
|
2020-05-07 21:00:36 +00:00
|
|
|
now := i.fs.clock.Now().Nanoseconds()
|
2020-04-03 02:37:41 +00:00
|
|
|
atomic.StoreInt64(&i.mtime, now)
|
|
|
|
atomic.StoreInt64(&i.ctime, now)
|
|
|
|
}
|
|
|
|
|
2020-04-11 02:01:39 +00:00
|
|
|
func (i *inode) listxattr(size uint64) ([]string, error) {
|
|
|
|
return i.xattrs.Listxattr(size)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *inode) getxattr(creds *auth.Credentials, opts *vfs.GetxattrOptions) (string, error) {
|
|
|
|
if err := i.checkPermissions(creds, vfs.MayRead); err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
if !strings.HasPrefix(opts.Name, linux.XATTR_USER_PREFIX) {
|
|
|
|
return "", syserror.EOPNOTSUPP
|
|
|
|
}
|
|
|
|
if !i.userXattrSupported() {
|
|
|
|
return "", syserror.ENODATA
|
|
|
|
}
|
|
|
|
return i.xattrs.Getxattr(opts)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *inode) setxattr(creds *auth.Credentials, opts *vfs.SetxattrOptions) error {
|
|
|
|
if err := i.checkPermissions(creds, vfs.MayWrite); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !strings.HasPrefix(opts.Name, linux.XATTR_USER_PREFIX) {
|
|
|
|
return syserror.EOPNOTSUPP
|
|
|
|
}
|
|
|
|
if !i.userXattrSupported() {
|
|
|
|
return syserror.EPERM
|
|
|
|
}
|
|
|
|
return i.xattrs.Setxattr(opts)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *inode) removexattr(creds *auth.Credentials, name string) error {
|
|
|
|
if err := i.checkPermissions(creds, vfs.MayWrite); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !strings.HasPrefix(name, linux.XATTR_USER_PREFIX) {
|
|
|
|
return syserror.EOPNOTSUPP
|
|
|
|
}
|
|
|
|
if !i.userXattrSupported() {
|
|
|
|
return syserror.EPERM
|
|
|
|
}
|
|
|
|
return i.xattrs.Removexattr(name)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Extended attributes in the user.* namespace are only supported for regular
|
|
|
|
// files and directories.
|
|
|
|
func (i *inode) userXattrSupported() bool {
|
|
|
|
filetype := linux.S_IFMT & atomic.LoadUint32(&i.mode)
|
|
|
|
return filetype == linux.S_IFREG || filetype == linux.S_IFDIR
|
|
|
|
}
|
|
|
|
|
2020-01-06 20:51:35 +00:00
|
|
|
// fileDescription is embedded by tmpfs implementations of
|
2019-07-18 22:09:14 +00:00
|
|
|
// vfs.FileDescriptionImpl.
|
|
|
|
type fileDescription struct {
|
|
|
|
vfsfd vfs.FileDescription
|
2019-08-16 17:18:58 +00:00
|
|
|
vfs.FileDescriptionDefaultImpl
|
2020-06-10 01:44:57 +00:00
|
|
|
vfs.LockFD
|
2019-07-18 22:09:14 +00:00
|
|
|
}
|
|
|
|
|
2019-08-08 18:45:33 +00:00
|
|
|
func (fd *fileDescription) filesystem() *filesystem {
|
Minor VFS2 interface changes.
- Remove the Filesystem argument from DentryImpl.*Ref(); in general DentryImpls
that need the Filesystem for reference counting will probably also need it
for other interface methods that don't plumb Filesystem, so it's easier to
just store a pointer to the filesystem in the DentryImpl.
- Add a pointer to the VirtualFilesystem to Filesystem, which is needed by the
gofer client to disown dentries for cache eviction triggered by dentry
reference count changes.
- Rename FilesystemType.NewFilesystem to GetFilesystem; in some cases (e.g.
sysfs, cgroupfs) it's much cleaner for there to be only one Filesystem that
is used by all mounts, and in at least one case (devtmpfs) it's visibly
incorrect not to do so, so NewFilesystem doesn't always actually create and
return a *new* Filesystem.
- Require callers of FileDescription.Init() to increment Mount/Dentry
references. This is because the gofer client may, in the OpenAt() path, take
a reference on a dentry with 0 references, which is safe due to
synchronization that is outside the scope of this CL, and it would be safer
to still have its implementation of DentryImpl.IncRef() check for an
increment for 0 references in other cases.
- Add FileDescription.TryIncRef. This is used by the gofer client to take
references on "special file descriptions" (FDs for files such as pipes,
sockets, and devices), which use per-FD handles (fids) instead of
dentry-shared handles, for sync() and syncfs().
PiperOrigin-RevId: 282473364
2019-11-26 02:09:15 +00:00
|
|
|
return fd.vfsfd.Mount().Filesystem().Impl().(*filesystem)
|
2019-07-18 22:09:14 +00:00
|
|
|
}
|
|
|
|
|
2020-05-29 15:07:44 +00:00
|
|
|
func (fd *fileDescription) dentry() *dentry {
|
|
|
|
return fd.vfsfd.Dentry().Impl().(*dentry)
|
|
|
|
}
|
|
|
|
|
2019-08-08 18:45:33 +00:00
|
|
|
func (fd *fileDescription) inode() *inode {
|
2020-05-29 15:07:44 +00:00
|
|
|
return fd.dentry().inode
|
2019-07-18 22:09:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Stat implements vfs.FileDescriptionImpl.Stat.
|
|
|
|
func (fd *fileDescription) Stat(ctx context.Context, opts vfs.StatOptions) (linux.Statx, error) {
|
|
|
|
var stat linux.Statx
|
|
|
|
fd.inode().statTo(&stat)
|
|
|
|
return stat, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetStat implements vfs.FileDescriptionImpl.SetStat.
|
|
|
|
func (fd *fileDescription) SetStat(ctx context.Context, opts vfs.SetStatOptions) error {
|
2020-03-16 20:28:00 +00:00
|
|
|
creds := auth.CredentialsFromContext(ctx)
|
2020-05-29 15:07:44 +00:00
|
|
|
d := fd.dentry()
|
|
|
|
if err := d.inode.setStat(ctx, creds, &opts.Stat); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if ev := vfs.InotifyEventFromStatMask(opts.Stat.Mask); ev != 0 {
|
2020-05-29 19:27:15 +00:00
|
|
|
d.InotifyWithParent(ev, 0, vfs.InodeEvent)
|
2020-05-29 15:07:44 +00:00
|
|
|
}
|
|
|
|
return nil
|
2019-07-18 22:09:14 +00:00
|
|
|
}
|
2020-04-11 02:01:39 +00:00
|
|
|
|
|
|
|
// Listxattr implements vfs.FileDescriptionImpl.Listxattr.
|
|
|
|
func (fd *fileDescription) Listxattr(ctx context.Context, size uint64) ([]string, error) {
|
|
|
|
return fd.inode().listxattr(size)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Getxattr implements vfs.FileDescriptionImpl.Getxattr.
|
|
|
|
func (fd *fileDescription) Getxattr(ctx context.Context, opts vfs.GetxattrOptions) (string, error) {
|
|
|
|
return fd.inode().getxattr(auth.CredentialsFromContext(ctx), &opts)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setxattr implements vfs.FileDescriptionImpl.Setxattr.
|
|
|
|
func (fd *fileDescription) Setxattr(ctx context.Context, opts vfs.SetxattrOptions) error {
|
2020-05-29 15:07:44 +00:00
|
|
|
d := fd.dentry()
|
|
|
|
if err := d.inode.setxattr(auth.CredentialsFromContext(ctx), &opts); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate inotify events.
|
2020-05-29 19:27:15 +00:00
|
|
|
d.InotifyWithParent(linux.IN_ATTRIB, 0, vfs.InodeEvent)
|
2020-05-29 15:07:44 +00:00
|
|
|
return nil
|
2020-04-11 02:01:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Removexattr implements vfs.FileDescriptionImpl.Removexattr.
|
|
|
|
func (fd *fileDescription) Removexattr(ctx context.Context, name string) error {
|
2020-05-29 15:07:44 +00:00
|
|
|
d := fd.dentry()
|
|
|
|
if err := d.inode.removexattr(auth.CredentialsFromContext(ctx), name); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate inotify events.
|
2020-05-29 19:27:15 +00:00
|
|
|
d.InotifyWithParent(linux.IN_ATTRIB, 0, vfs.InodeEvent)
|
2020-05-29 15:07:44 +00:00
|
|
|
return nil
|
2020-04-11 02:01:39 +00:00
|
|
|
}
|
2020-05-14 16:34:21 +00:00
|
|
|
|
|
|
|
// NewMemfd creates a new tmpfs regular file and file description that can back
|
|
|
|
// an anonymous fd created by memfd_create.
|
|
|
|
func NewMemfd(mount *vfs.Mount, creds *auth.Credentials, allowSeals bool, name string) (*vfs.FileDescription, error) {
|
|
|
|
fs, ok := mount.Filesystem().Impl().(*filesystem)
|
|
|
|
if !ok {
|
|
|
|
panic("NewMemfd() called with non-tmpfs mount")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Per Linux, mm/shmem.c:__shmem_file_setup(), memfd inodes are set up with
|
|
|
|
// S_IRWXUGO.
|
2020-06-08 20:27:06 +00:00
|
|
|
inode := fs.newRegularFile(creds.EffectiveKUID, creds.EffectiveKGID, 0777)
|
2020-05-14 16:34:21 +00:00
|
|
|
rf := inode.impl.(*regularFile)
|
|
|
|
if allowSeals {
|
|
|
|
rf.seals = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
d := fs.newDentry(inode)
|
|
|
|
defer d.DecRef()
|
|
|
|
d.name = name
|
|
|
|
|
|
|
|
// Per Linux, mm/shmem.c:__shmem_file_setup(), memfd files are set up with
|
|
|
|
// FMODE_READ | FMODE_WRITE.
|
|
|
|
var fd regularFileFD
|
|
|
|
flags := uint32(linux.O_RDWR)
|
|
|
|
if err := fd.vfsfd.Init(&fd, flags, mount, &d.vfsd, &vfs.FileDescriptionOptions{}); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &fd.vfsfd, nil
|
|
|
|
}
|