diff options
-rw-r--r-- | runsc/fsgofer/fsgofer.go | 128 |
1 files changed, 88 insertions, 40 deletions
diff --git a/runsc/fsgofer/fsgofer.go b/runsc/fsgofer/fsgofer.go index 1316dc618..b325afa63 100644 --- a/runsc/fsgofer/fsgofer.go +++ b/runsc/fsgofer/fsgofer.go @@ -91,14 +91,29 @@ type attachPoint struct { prefix string conf Config - mu sync.Mutex - attached bool + // attachedMu protects attached. + attachedMu sync.Mutex + attached bool + + // deviceMu protects devices and nextDevice. + deviceMu sync.Mutex + + // nextDevice is the next device id that will be allocated. + nextDevice uint8 + + // devices is a map from actual host devices to "small" integers that + // can be combined with host inode to form a unique virtual inode id. + devices map[uint64]uint8 } // NewAttachPoint creates a new attacher that gives local file // access to all files under 'prefix'. func NewAttachPoint(prefix string, c Config) p9.Attacher { - return &attachPoint{prefix: prefix, conf: c} + return &attachPoint{ + prefix: prefix, + conf: c, + devices: make(map[uint64]uint8), + } } // Attach implements p9.Attacher. @@ -131,21 +146,45 @@ func (a *attachPoint) Attach(appPath string) (p9.File, error) { return nil, fmt.Errorf("failed to stat file %q, err: %v", root, err) } - a.mu.Lock() - defer a.mu.Unlock() + a.attachedMu.Lock() + defer a.attachedMu.Unlock() if a.attached { f.Close() return nil, fmt.Errorf("attach point already attached, prefix: %s", a.prefix) } a.attached = true - return newLocalFile(a.conf, f, root, stat) + return newLocalFile(a, f, root, stat) } -func makeQID(stat syscall.Stat_t) p9.QID { +// makeQID returns a unique QID for the given stat buffer. +func (a *attachPoint) makeQID(stat syscall.Stat_t) p9.QID { + a.deviceMu.Lock() + defer a.deviceMu.Unlock() + + // First map the host device id to a unique 8-bit integer. + dev, ok := a.devices[stat.Dev] + if !ok { + a.devices[stat.Dev] = a.nextDevice + dev = a.nextDevice + a.nextDevice++ + if a.nextDevice < dev { + panic(fmt.Sprintf("device id overflow! map: %+v", a.devices)) + } + } + + // Construct a "virtual" inode id with the uint8 device number in the + // first 8 bits, and the rest of the bits from the host inode id. + maskedIno := stat.Ino & 0x00ffffffffffffff + if maskedIno != stat.Ino { + log.Warningf("first 8 bytes of host inode id %x will be truncated to construct virtual inode id", stat.Ino) + } + ino := uint64(dev)<<56 | maskedIno + log.Debugf("host inode %x on device %x mapped to virtual inode %x", stat.Ino, stat.Dev, ino) + return p9.QID{ Type: p9.FileMode(stat.Mode).QIDType(), - Path: stat.Ino, + Path: ino, } } @@ -193,6 +232,9 @@ func isNameValid(name string) bool { type localFile struct { p9.DefaultWalkGetAttr + // attachPoint is the attachPoint that serves this localFile. + attachPoint *attachPoint + // mu protects 'hostPath' when file is renamed. mu sync.Mutex @@ -213,8 +255,6 @@ type localFile struct { ft fileType - conf Config - // readDirMu protects against concurrent Readdir calls. readDirMu sync.Mutex } @@ -228,7 +268,8 @@ func openAnyFile(parent *localFile, name string) (*os.File, string, error) { symlinkIdx := len(modes) - 1 startIdx := 0 - if parent.conf.ROMount || parent.conf.LazyOpenForWrite { + conf := parent.attachPoint.conf + if conf.ROMount || conf.LazyOpenForWrite { // Skip attempt to open in RDWR based on configuration. startIdx = 1 } @@ -269,7 +310,7 @@ func openAnyFile(parent *localFile, name string) (*os.File, string, error) { return os.NewFile(uintptr(fd), newPath), newPath, nil } -func newLocalFile(conf Config, file *os.File, path string, stat syscall.Stat_t) (*localFile, error) { +func newLocalFile(a *attachPoint, file *os.File, path string, stat syscall.Stat_t) (*localFile, error) { var ft fileType switch stat.Mode & syscall.S_IFMT { case syscall.S_IFREG: @@ -282,9 +323,9 @@ func newLocalFile(conf Config, file *os.File, path string, stat syscall.Stat_t) return nil, syscall.EINVAL } return &localFile{ + attachPoint: a, hostPath: path, controlFile: file, - conf: conf, mode: invalidMode, ft: ft, }, nil @@ -338,7 +379,7 @@ func (l *localFile) Open(mode p9.OpenFlags) (*fd.FD, p9.QID, uint32, error) { // Check if control file can be used or if a new open must be created. var newFile *os.File - if mode == p9.ReadOnly || !l.conf.LazyOpenForWrite { + if mode == p9.ReadOnly || !l.attachPoint.conf.LazyOpenForWrite { log.Debugf("Open reusing control file, mode: %v, %q", mode, l.controlFile.Name()) newFile = l.controlFile } else { @@ -372,13 +413,14 @@ func (l *localFile) Open(mode p9.OpenFlags) (*fd.FD, p9.QID, uint32, error) { // Set fields on success l.openedFile = newFile l.mode = mode - return fd, makeQID(stat), 0, nil + return fd, l.attachPoint.makeQID(stat), 0, nil } // Create implements p9.File. func (l *localFile) Create(name string, mode p9.OpenFlags, perm p9.FileMode, uid p9.UID, gid p9.GID) (*fd.FD, p9.File, p9.QID, uint32, error) { - if l.conf.ROMount { - if l.conf.PanicOnWrite { + conf := l.attachPoint.conf + if conf.ROMount { + if conf.PanicOnWrite { panic("attempt to write to RO mount") } return nil, nil, p9.QID{}, 0, syscall.EBADF @@ -423,19 +465,20 @@ func (l *localFile) Create(name string, mode p9.OpenFlags, perm p9.FileMode, uid cPath := path.Join(l.hostPath, name) f := os.NewFile(uintptr(fd), cPath) c := &localFile{ + attachPoint: l.attachPoint, hostPath: cPath, controlFile: f, openedFile: f, mode: mode, - conf: l.conf, } - return newFDMaybe(c.openedFile), c, makeQID(stat), 0, nil + return newFDMaybe(c.openedFile), c, l.attachPoint.makeQID(stat), 0, nil } // Mkdir implements p9.File. func (l *localFile) Mkdir(name string, perm p9.FileMode, uid p9.UID, gid p9.GID) (p9.QID, error) { - if l.conf.ROMount { - if l.conf.PanicOnWrite { + conf := l.attachPoint.conf + if conf.ROMount { + if conf.PanicOnWrite { panic("attempt to write to RO mount") } return p9.QID{}, syscall.EBADF @@ -464,7 +507,7 @@ func (l *localFile) Mkdir(name string, perm p9.FileMode, uid p9.UID, gid p9.GID) if err != nil { return p9.QID{}, extractErrno(err) } - return makeQID(stat), nil + return l.attachPoint.makeQID(stat), nil } // Walk implements p9.File. @@ -485,12 +528,12 @@ func (l *localFile) Walk(names []string) ([]p9.QID, p9.File, error) { defer l.mu.Unlock() c := &localFile{ + attachPoint: l.attachPoint, hostPath: l.hostPath, controlFile: os.NewFile(uintptr(newFd), l.hostPath), mode: invalidMode, - conf: l.conf, } - return []p9.QID{makeQID(stat)}, c, nil + return []p9.QID{l.attachPoint.makeQID(stat)}, c, nil } var qids []p9.QID @@ -508,12 +551,12 @@ func (l *localFile) Walk(names []string) ([]p9.QID, p9.File, error) { if err != nil { return nil, nil, extractErrno(err) } - c, err := newLocalFile(last.conf, f, path, stat) + c, err := newLocalFile(last.attachPoint, f, path, stat) if err != nil { return nil, nil, extractErrno(err) } - qids = append(qids, makeQID(stat)) + qids = append(qids, l.attachPoint.makeQID(stat)) last = c } return qids, last, nil @@ -586,15 +629,16 @@ func (l *localFile) GetAttr(_ p9.AttrMask) (p9.QID, p9.AttrMask, p9.Attr, error) CTime: true, } - return makeQID(stat), valid, attr, nil + return l.attachPoint.makeQID(stat), valid, attr, nil } // SetAttr implements p9.File. Due to mismatch in file API, options // cannot be changed atomicaly and user may see partial changes when // an error happens. func (l *localFile) SetAttr(valid p9.SetAttrMask, attr p9.SetAttr) error { - if l.conf.ROMount { - if l.conf.PanicOnWrite { + conf := l.attachPoint.conf + if conf.ROMount { + if conf.PanicOnWrite { panic("attempt to write to RO mount") } return syscall.EBADF @@ -624,7 +668,7 @@ func (l *localFile) SetAttr(valid p9.SetAttrMask, attr p9.SetAttr) error { } fd := l.controlFD() - if l.conf.LazyOpenForWrite && l.ft == regular { + if conf.LazyOpenForWrite && l.ft == regular { // Regular files are opened in RO mode when lazy open is set. // Thus it needs to be reopened here for write. f, err := os.OpenFile(l.hostPath, openFlags|os.O_WRONLY, 0) @@ -733,8 +777,9 @@ func (*localFile) Remove() error { // Rename implements p9.File. func (l *localFile) Rename(directory p9.File, name string) error { - if l.conf.ROMount { - if l.conf.PanicOnWrite { + conf := l.attachPoint.conf + if conf.ROMount { + if conf.PanicOnWrite { panic("attempt to write to RO mount") } return syscall.EBADF @@ -803,8 +848,9 @@ func (l *localFile) WriteAt(p []byte, offset uint64) (int, error) { // Symlink implements p9.File. func (l *localFile) Symlink(target, newName string, uid p9.UID, gid p9.GID) (p9.QID, error) { - if l.conf.ROMount { - if l.conf.PanicOnWrite { + conf := l.attachPoint.conf + if conf.ROMount { + if conf.PanicOnWrite { panic("attempt to write to RO mount") } return p9.QID{}, syscall.EBADF @@ -831,13 +877,14 @@ func (l *localFile) Symlink(target, newName string, uid p9.UID, gid p9.GID) (p9. if err != nil { return p9.QID{}, extractErrno(err) } - return makeQID(stat), nil + return l.attachPoint.makeQID(stat), nil } // Link implements p9.File. func (l *localFile) Link(target p9.File, newName string) error { - if l.conf.ROMount { - if l.conf.PanicOnWrite { + conf := l.attachPoint.conf + if conf.ROMount { + if conf.PanicOnWrite { panic("attempt to write to RO mount") } return syscall.EBADF @@ -862,8 +909,9 @@ func (*localFile) Mknod(_ string, _ p9.FileMode, _ uint32, _ uint32, _ p9.UID, _ // UnlinkAt implements p9.File. func (l *localFile) UnlinkAt(name string, flags uint32) error { - if l.conf.ROMount { - if l.conf.PanicOnWrite { + conf := l.attachPoint.conf + if conf.ROMount { + if conf.PanicOnWrite { panic("attempt to write to RO mount") } return syscall.EBADF @@ -906,7 +954,7 @@ func (l *localFile) Readdir(offset uint64, count uint32) ([]p9.Dirent, error) { if err != nil { continue } - qid := makeQID(stat) + qid := l.attachPoint.makeQID(stat) dirents = append(dirents, p9.Dirent{ QID: qid, Type: qid.Type, |