292 lines
7.1 KiB
Go
292 lines
7.1 KiB
Go
// Copyright 2018 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.
|
|
|
|
// Package localfs exposes the host's local file system as a p9.File.
|
|
package localfs
|
|
|
|
import (
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
|
|
"github.com/hugelgupf/p9/fsimpl/templatefs"
|
|
"github.com/hugelgupf/p9/internal"
|
|
"github.com/hugelgupf/p9/linux"
|
|
"github.com/hugelgupf/p9/p9"
|
|
)
|
|
|
|
type attacher struct {
|
|
root string
|
|
}
|
|
|
|
var (
|
|
_ p9.Attacher = &attacher{}
|
|
)
|
|
|
|
// RootAttacher attaches at the host file system's root.
|
|
func RootAttacher() p9.Attacher {
|
|
return &attacher{root: "/"}
|
|
}
|
|
|
|
// Attacher returns an attacher that exposes files under root.
|
|
func Attacher(root string) p9.Attacher {
|
|
if len(root) == 0 {
|
|
root = "/"
|
|
}
|
|
return &attacher{root: root}
|
|
}
|
|
|
|
// Attach implements p9.Attacher.Attach.
|
|
func (a *attacher) Attach() (p9.File, error) {
|
|
umask(0)
|
|
return &Local{path: a.root}, nil
|
|
}
|
|
|
|
// Local is a p9.File.
|
|
type Local struct {
|
|
p9.DefaultWalkGetAttr
|
|
templatefs.NoopFile
|
|
|
|
path string
|
|
file *os.File
|
|
}
|
|
|
|
var (
|
|
_ p9.File = &Local{}
|
|
)
|
|
|
|
// info constructs a QID for this file.
|
|
func (l *Local) info() (p9.QID, os.FileInfo, error) {
|
|
var (
|
|
qid p9.QID
|
|
fi os.FileInfo
|
|
err error
|
|
)
|
|
|
|
// Stat the file.
|
|
if l.file != nil {
|
|
fi, err = l.file.Stat()
|
|
} else {
|
|
fi, err = os.Lstat(l.path)
|
|
}
|
|
if err != nil {
|
|
return qid, nil, err
|
|
}
|
|
|
|
// Construct the QID type.
|
|
qid.Type = p9.ModeFromOS(fi.Mode()).QIDType()
|
|
|
|
// Save the path from the Ino.
|
|
ninePath, err := localToQid(l.path, fi)
|
|
if err != nil {
|
|
return qid, nil, err
|
|
}
|
|
|
|
qid.Path = ninePath
|
|
|
|
return qid, fi, nil
|
|
}
|
|
|
|
// Walk implements p9.File.Walk.
|
|
func (l *Local) Walk(names []string) ([]p9.QID, p9.File, error) {
|
|
var qids []p9.QID
|
|
last := &Local{path: l.path}
|
|
|
|
// A walk with no names is a copy of self.
|
|
if len(names) == 0 {
|
|
return nil, last, nil
|
|
}
|
|
|
|
for _, name := range names {
|
|
c := &Local{path: path.Join(last.path, name)}
|
|
qid, _, err := c.info()
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
qids = append(qids, qid)
|
|
last = c
|
|
}
|
|
return qids, last, nil
|
|
}
|
|
|
|
// FSync implements p9.File.FSync.
|
|
func (l *Local) FSync() error {
|
|
return l.file.Sync()
|
|
}
|
|
|
|
// GetAttr implements p9.File.GetAttr.
|
|
//
|
|
// Not fully implemented.
|
|
func (l *Local) GetAttr(req p9.AttrMask) (p9.QID, p9.AttrMask, p9.Attr, error) {
|
|
qid, fi, err := l.info()
|
|
if err != nil {
|
|
return qid, p9.AttrMask{}, p9.Attr{}, err
|
|
}
|
|
|
|
stat := internal.InfoToStat(fi)
|
|
attr := &p9.Attr{
|
|
Mode: p9.FileMode(stat.Mode),
|
|
UID: p9.UID(stat.Uid),
|
|
GID: p9.GID(stat.Gid),
|
|
NLink: p9.NLink(stat.Nlink),
|
|
RDev: p9.Dev(stat.Rdev),
|
|
Size: uint64(stat.Size),
|
|
BlockSize: uint64(stat.Blksize),
|
|
Blocks: uint64(stat.Blocks),
|
|
ATimeSeconds: uint64(stat.Atim.Sec),
|
|
ATimeNanoSeconds: uint64(stat.Atim.Nsec),
|
|
MTimeSeconds: uint64(stat.Mtim.Sec),
|
|
MTimeNanoSeconds: uint64(stat.Mtim.Nsec),
|
|
CTimeSeconds: uint64(stat.Ctim.Sec),
|
|
CTimeNanoSeconds: uint64(stat.Ctim.Nsec),
|
|
}
|
|
return qid, req, *attr, nil
|
|
}
|
|
|
|
// Close implements p9.File.Close.
|
|
func (l *Local) Close() error {
|
|
if l.file != nil {
|
|
// We don't set l.file = nil, as Close is called by servers
|
|
// only in Clunk. Clunk should release the last (direct)
|
|
// reference to this file.
|
|
return l.file.Close()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Open implements p9.File.Open.
|
|
func (l *Local) Open(mode p9.OpenFlags) (p9.QID, uint32, error) {
|
|
qid, _, err := l.info()
|
|
if err != nil {
|
|
return qid, 0, err
|
|
}
|
|
|
|
// Do the actual open.
|
|
f, err := os.OpenFile(l.path, int(mode), 0)
|
|
if err != nil {
|
|
return qid, 0, err
|
|
}
|
|
l.file = f
|
|
|
|
return qid, 0, nil
|
|
}
|
|
|
|
// ReadAt implements p9.File.ReadAt.
|
|
func (l *Local) ReadAt(p []byte, offset int64) (int, error) {
|
|
return l.file.ReadAt(p, offset)
|
|
}
|
|
|
|
// Lock implements p9.File.Lock.
|
|
func (l *Local) Lock(pid int, locktype p9.LockType, flags p9.LockFlags, start, length uint64, client string) (p9.LockStatus, error) {
|
|
return l.lock(pid, locktype, flags, start, length, client)
|
|
}
|
|
|
|
// WriteAt implements p9.File.WriteAt.
|
|
func (l *Local) WriteAt(p []byte, offset int64) (int, error) {
|
|
return l.file.WriteAt(p, offset)
|
|
}
|
|
|
|
// Create implements p9.File.Create.
|
|
func (l *Local) Create(name string, mode p9.OpenFlags, permissions p9.FileMode, _ p9.UID, _ p9.GID) (p9.File, p9.QID, uint32, error) {
|
|
newName := path.Join(l.path, name)
|
|
f, err := os.OpenFile(newName, int(mode)|os.O_CREATE|os.O_EXCL, os.FileMode(permissions))
|
|
if err != nil {
|
|
return nil, p9.QID{}, 0, err
|
|
}
|
|
|
|
l2 := &Local{path: newName, file: f}
|
|
qid, _, err := l2.info()
|
|
if err != nil {
|
|
l2.Close()
|
|
return nil, p9.QID{}, 0, err
|
|
}
|
|
return l2, qid, 0, nil
|
|
}
|
|
|
|
// Mkdir implements p9.File.Mkdir.
|
|
//
|
|
// Not properly implemented.
|
|
func (l *Local) Mkdir(name string, permissions p9.FileMode, _ p9.UID, _ p9.GID) (p9.QID, error) {
|
|
if err := os.Mkdir(path.Join(l.path, name), os.FileMode(permissions)); err != nil {
|
|
return p9.QID{}, err
|
|
}
|
|
|
|
// Blank QID.
|
|
return p9.QID{}, nil
|
|
}
|
|
|
|
// Symlink implements p9.File.Symlink.
|
|
//
|
|
// Not properly implemented.
|
|
func (l *Local) Symlink(oldname string, newname string, _ p9.UID, _ p9.GID) (p9.QID, error) {
|
|
if err := os.Symlink(oldname, path.Join(l.path, newname)); err != nil {
|
|
return p9.QID{}, err
|
|
}
|
|
|
|
// Blank QID.
|
|
return p9.QID{}, nil
|
|
}
|
|
|
|
// Link implements p9.File.Link.
|
|
//
|
|
// Not properly implemented.
|
|
func (l *Local) Link(target p9.File, newname string) error {
|
|
return os.Link(target.(*Local).path, path.Join(l.path, newname))
|
|
}
|
|
|
|
// RenameAt implements p9.File.RenameAt.
|
|
func (l *Local) RenameAt(oldName string, newDir p9.File, newName string) error {
|
|
oldPath := path.Join(l.path, oldName)
|
|
newPath := path.Join(newDir.(*Local).path, newName)
|
|
|
|
return os.Rename(oldPath, newPath)
|
|
}
|
|
|
|
// Readlink implements p9.File.Readlink.
|
|
//
|
|
// Not properly implemented.
|
|
func (l *Local) Readlink() (string, error) {
|
|
return os.Readlink(l.path)
|
|
}
|
|
|
|
// Renamed implements p9.File.Renamed.
|
|
func (l *Local) Renamed(parent p9.File, newName string) {
|
|
l.path = path.Join(parent.(*Local).path, newName)
|
|
}
|
|
|
|
// SetAttr implements p9.File.SetAttr.
|
|
func (l *Local) SetAttr(valid p9.SetAttrMask, attr p9.SetAttr) error {
|
|
// When truncate(2) is called on Linux, Linux will try to set time & size. Fake it. Sorry.
|
|
supported := p9.SetAttrMask{Size: true, MTime: true, CTime: true, ATime: true}
|
|
if !valid.IsSubsetOf(supported) {
|
|
return linux.ENOSYS
|
|
}
|
|
|
|
if valid.Size {
|
|
// If more than one thing is ever implemented, we can't just
|
|
// return an error here.
|
|
return os.Truncate(l.path, int64(attr.Size))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// UnlinkAt implements p9.File.UnlinkAt
|
|
func (l *Local) UnlinkAt(name string, flags uint32) error {
|
|
// Construct the full path
|
|
fullPath := filepath.Join(l.path, name)
|
|
|
|
// Remove the file or directory
|
|
return os.Remove(fullPath)
|
|
}
|