diff --git a/client/cpio9p.go b/client/cpio9p.go index 4739d0e6..3fc766da 100644 --- a/client/cpio9p.go +++ b/client/cpio9p.go @@ -15,12 +15,12 @@ package client import ( + "errors" "fmt" "io" "io/fs" "os" "path/filepath" - "syscall" "github.com/hugelgupf/p9/fsimpl/templatefs" "github.com/hugelgupf/p9/p9" @@ -59,6 +59,9 @@ func NewCPIO9P(c string) (*CPIO9P, error) { return NewCPIO9PReaderAt(f) } +// ErrNosys is the kernel-independent ENOSYS +var ErrNosys = errors.New("function not implemented") + // NewCPIO9PReaderAt returns a CPIO9P, properly initialized, from an io.ReaderAt. func NewCPIO9PReaderAt(r io.ReaderAt) (*CPIO9P, error) { archive, err := cpio.Format("newc") @@ -351,25 +354,25 @@ func (l *CPIO9PFID) UnlinkAt(name string, flags uint32) error { // Mknod implements p9.File.Mknod. func (*CPIO9PFID) Mknod(name string, mode p9.FileMode, major uint32, minor uint32, _ p9.UID, _ p9.GID) (p9.QID, error) { - return p9.QID{}, syscall.ENOSYS + return p9.QID{}, ErrNosys } // Rename implements p9.File.Rename. func (*CPIO9PFID) Rename(directory p9.File, name string) error { - return syscall.ENOSYS + return ErrNosys } // RenameAt implements p9.File.RenameAt. // There is no guarantee that there is not a zipslip issue. func (l *CPIO9PFID) RenameAt(oldName string, newDir p9.File, newName string) error { - return syscall.ENOSYS + return ErrNosys } // StatFS implements p9.File.StatFS. // // Not implemented. func (*CPIO9PFID) StatFS() (p9.FSStat, error) { - return p9.FSStat{}, syscall.ENOSYS + return p9.FSStat{}, ErrNosys } // SetAttr implements SetAttr. diff --git a/client/cpu9p.go b/client/cpu9p.go index 11999a12..6b88deab 100644 --- a/client/cpu9p.go +++ b/client/cpu9p.go @@ -20,11 +20,8 @@ import ( "path" "path/filepath" "strings" - "syscall" - "github.com/hugelgupf/p9/fsimpl/xattr" "github.com/hugelgupf/p9/p9" - "golang.org/x/sys/unix" ) // CPU9P is a p9.Attacher. @@ -50,53 +47,6 @@ var ( _ p9.Attacher = &CPU9P{} ) -// info constructs a QID for this file. -func (l *CPU9P) 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 { - //log.Printf("error stating %#v: %v", l, err) - return qid, nil, err - } - - // Construct the QID type. - qid.Type = p9.ModeFromOS(fi.Mode()).QIDType() - - // Save the path from the Ino. - qid.Path = fi.Sys().(*syscall.Stat_t).Ino - return qid, fi, nil -} - -// SetXattr implements p9.File.SetXattr -func (l *CPU9P) SetXattr(attr string, data []byte, flags p9.XattrFlags) error { - return unix.Setxattr(l.path, attr, data, int(flags)) -} - -// ListXattrs implements p9.File.ListXattrs -func (l *CPU9P) ListXattrs() ([]string, error) { - return xattr.List(l.path) -} - -// GetXattr implements p9.File.GetXattr -func (l *CPU9P) GetXattr(attr string) ([]byte, error) { - return xattr.Get(l.path, attr) -} - -// RemoveXattr implements p9.File.RemoveXattr -func (l *CPU9P) RemoveXattr(attr string) error { - return unix.Removexattr(l.path, attr) -} - // Walk implements p9.File.Walk. func (l *CPU9P) Walk(names []string) ([]p9.QID, p9.File, error) { var qids []p9.QID @@ -303,13 +253,13 @@ func (l *CPU9P) UnlinkAt(name string, flags uint32) error { // Mknod implements p9.File.Mknod. func (*CPU9P) Mknod(name string, mode p9.FileMode, major uint32, minor uint32, _ p9.UID, _ p9.GID) (p9.QID, error) { verbose("Mknod: not implemented") - return p9.QID{}, syscall.ENOSYS + return p9.QID{}, ErrNosys } // Rename implements p9.File.Rename. func (*CPU9P) Rename(directory p9.File, name string) error { verbose("Rename: not implemented") - return syscall.ENOSYS + return ErrNosys } // RenameAt implements p9.File.RenameAt. @@ -333,5 +283,5 @@ func (l *CPU9P) RenameAt(oldName string, newDir p9.File, newName string) error { // Not implemented. func (*CPU9P) StatFS() (p9.FSStat, error) { verbose("StatFS: not implemented") - return p9.FSStat{}, syscall.ENOSYS + return p9.FSStat{}, ErrNosys } diff --git a/client/cpu9p_plan9.go b/client/cpu9p_plan9.go new file mode 100644 index 00000000..db55d890 --- /dev/null +++ b/client/cpu9p_plan9.go @@ -0,0 +1,129 @@ +// Copyright 2022 the u-root Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package client + +import ( + "errors" + "fmt" + "os" + "syscall" + "time" + + "github.com/hugelgupf/p9/p9" +) + +// ErrNoDirInfo means Sys() was empty after a Stat() +var ErrNoDirInfo = errors.New("no Sys() after a Stat()") + +// SetAttr implements p9.File.SetAttr. +func (l *CPU9P) SetAttr(mask p9.SetAttrMask, attr p9.SetAttr) error { + var err error + // Any or ALL can be set. + // A setattr could include things to set, + // and a permission value that makes setting those + // things impossible. Therefore, do these + // permission-y things last: + // Permissions + // GID + // UID + // Since changing, e.g., UID or GID might make + // changing permissions impossible. + // + // The test actually caught this ... + + if mask.Size { + if e := os.Truncate(l.path, int64(attr.Size)); e != nil { + err = errors.Join(err, fmt.Errorf("truncate:%w", err)) + } + } + if mask.ATime || mask.MTime { + atime, mtime := time.Now(), time.Now() + if mask.ATimeNotSystemTime { + atime = time.Unix(int64(attr.ATimeSeconds), int64(attr.ATimeNanoSeconds)) + } + if mask.MTimeNotSystemTime { + mtime = time.Unix(int64(attr.MTimeSeconds), int64(attr.MTimeNanoSeconds)) + } + if e := os.Chtimes(l.path, atime, mtime); e != nil { + err = errors.Join(err, e) + } + } + + if mask.CTime { + // The Linux client sets CTime. I did not even know that was allowed. + // if e := errors.New("Can not set CTime on Unix"); e != nil { err = errors.Join(e)} + verbose("mask.CTime is set by client; ignoring") + } + if mask.Permissions { + perm := uint32(attr.Permissions) + if e := os.Chmod(l.path, os.FileMode(perm)); e != nil { + err = errors.Join(err, fmt.Errorf("%q:%o:%w", l.path, perm, err)) + } + } + + if mask.GID { + err = errors.Join(err, os.ErrPermission) + } + if mask.UID { + err = errors.Join(err, os.ErrPermission) + } + return err +} + +// Lock implements p9.File.Lock. +// No such thing on Plan 9. Just say ok. +func (l *CPU9P) Lock(pid int, locktype p9.LockType, flags p9.LockFlags, start, length uint64, client string) (p9.LockStatus, error) { + return p9.LockStatusOK, nil +} + +// info constructs a QID for this file. +func (l *CPU9P) 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() + } + + if err != nil { + return qid, nil, err + } + + d, ok := fi.Sys().(*syscall.Dir) + if !ok { + return qid, fi, fmt.Errorf("%q:%v", l.file.Name(), ErrNoDirInfo) + + } + + qid.Path, qid.Version, qid.Type = d.Qid.Path, d.Qid.Vers, p9.QIDType(d.Qid.Type) + + return qid, fi, nil +} + +// SetXattr implements p9.File.SetXattr +func (l *CPU9P) SetXattr(attr string, data []byte, flags p9.XattrFlags) error { + return ErrNosys +} + +// ListXattrs implements p9.File.ListXattrs +// Since there technically are none, return an empty []string and +// no error. +func (l *CPU9P) ListXattrs() ([]string, error) { + return []string{}, nil +} + +// GetXattr implements p9.File.GetXattr +func (l *CPU9P) GetXattr(attr string) ([]byte, error) { + return nil, nil +} + +// RemoveXattr implements p9.File.RemoveXattr +func (l *CPU9P) RemoveXattr(attr string) error { + return ErrNosys +} diff --git a/client/cpu9p_unix.go b/client/cpu9p_unix.go index e0dfe80a..3adabc0b 100644 --- a/client/cpu9p_unix.go +++ b/client/cpu9p_unix.go @@ -11,8 +11,10 @@ import ( "errors" "fmt" "os" + "syscall" "time" + "github.com/hugelgupf/p9/fsimpl/xattr" "github.com/hugelgupf/p9/p9" "golang.org/x/sys/unix" ) @@ -112,3 +114,50 @@ func (l *CPU9P) Lock(pid int, locktype p9.LockType, flags p9.LockFlags, start, l } return p9.LockStatusOK, nil } + +// info constructs a QID for this file. +func (l *CPU9P) 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 { + //log.Printf("error stating %#v: %v", l, err) + return qid, nil, err + } + + // Construct the QID type. + qid.Type = p9.ModeFromOS(fi.Mode()).QIDType() + + // Save the path from the Ino. + qid.Path = fi.Sys().(*syscall.Stat_t).Ino + return qid, fi, nil +} + +// SetXattr implements p9.File.SetXattr +func (l *CPU9P) SetXattr(attr string, data []byte, flags p9.XattrFlags) error { + return unix.Setxattr(l.path, attr, data, int(flags)) +} + +// ListXattrs implements p9.File.ListXattrs +func (l *CPU9P) ListXattrs() ([]string, error) { + return xattr.List(l.path) +} + +// GetXattr implements p9.File.GetXattr +func (l *CPU9P) GetXattr(attr string) ([]byte, error) { + return xattr.Get(l.path, attr) +} + +// RemoveXattr implements p9.File.RemoveXattr +func (l *CPU9P) RemoveXattr(attr string) error { + return unix.Removexattr(l.path, attr) +} diff --git a/client/cpu_plan9.go b/client/cpu_plan9.go new file mode 100644 index 00000000..b3a5d965 --- /dev/null +++ b/client/cpu_plan9.go @@ -0,0 +1,19 @@ +// Copyright 2018-2019 the u-root Authors. All rights reserved +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package client + +import ( + "os" + + "github.com/hugelgupf/p9/p9" +) + +func osflags(fi os.FileInfo, mode p9.OpenFlags) int { + flags := int(mode) + + // special processing goes here. + + return flags +} diff --git a/client/fns.go b/client/fns.go index 8f7754df..6f00d57f 100644 --- a/client/fns.go +++ b/client/fns.go @@ -12,6 +12,7 @@ import ( "io" "os" "path/filepath" + "runtime" "strconv" "strings" @@ -118,7 +119,20 @@ func (c *Cmd) SetEnv(envs ...string) error { if len(env) == 1 { env = append(env, "") } - if err := c.session.Setenv(env[0], env[1]); err != nil { + // Plan 9 rc shell adds a NUL to environment variables. + // This will cause go exec to fail, due to a security + // mitigation, see + // Reject NUL in environment variables to prevent security issues (#56284) + // If we are on Plan 9, trim trailing NUL. + v := env[1] + if runtime.GOOS == "plan9" { + v = strings.TrimRight(v, "\x00") + if strings.Contains(v, "\x00") { + continue + } + } + verbose("c.session.Setenv(%q,%q)", env[0], v) + if err := c.session.Setenv(env[0], v); err != nil { err = errors.Join(fmt.Errorf("Warning: c.session.Setenv(%q, %q): %v", v, os.Getenv(v), err)) } } @@ -324,7 +338,9 @@ func ParseBinds(s string) (string, error) { // The convention is that the remote side is relative to filepath.Join(tmpMnt, "cpu") // and the left side is taken exactly as written. Further, recall that in bind mounts, the // remote side is the "device", and the local side is the "target." - fstab = fstab + fmt.Sprintf("%s %s none defaults,bind 0 0\n", filepath.Join(tmpMnt, "cpu", remote), local) + el := fmt.Sprintf("%s %s none defaults,bind 0 0\n", filepath.Join(tmpMnt, "cpu", remote), local) + verbose("Set up fstab element %s", el) + fstab = fstab + el } return fstab, nil } diff --git a/client/getattr_plan9.go b/client/getattr_plan9.go new file mode 100644 index 00000000..049a56f7 --- /dev/null +++ b/client/getattr_plan9.go @@ -0,0 +1,62 @@ +// 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 client + +import ( + "syscall" + + "github.com/hugelgupf/p9/p9" +) + +// GetAttr implements p9.File.GetAttr. +// +// Not fully implemented. +func (l *CPU9P) 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 + } + + s, ns := fi.ModTime().Second(), fi.ModTime().Nanosecond() + + attr := p9.Attr{ + Mode: p9.FileMode(fi.Mode()), + Size: uint64(fi.Size()), + BlockSize: 8192, + Blocks: uint64(fi.Size()) / 8192, + MTimeSeconds: uint64(s), + MTimeNanoSeconds: uint64(ns), + } + + valid := p9.AttrMask{ + Mode: true, + UID: false, + GID: false, + NLink: false, + RDev: false, + Size: true, + Blocks: true, + ATime: false, + MTime: true, + CTime: false, + } + + if d, ok := fi.Sys().(*syscall.Dir); ok { + valid.ATime, attr.ATimeSeconds, attr.ATimeNanoSeconds = true, uint64(d.Atime), 0 + valid.RDev, attr.RDev = true, p9.Dev(d.Dev) + } + + return qid, valid, attr, nil +} diff --git a/client/union.go b/client/union.go index 1342b013..dbb7cb36 100644 --- a/client/union.go +++ b/client/union.go @@ -25,6 +25,7 @@ package client import ( "context" + "errors" "fmt" "io" "io/fs" @@ -35,7 +36,6 @@ import ( "strconv" "strings" "sync/atomic" - "syscall" "time" "github.com/go-git/go-billy/v5" @@ -45,6 +45,9 @@ import ( nfshelper "github.com/willscott/go-nfs/helpers" ) +// ErrLoop is the kernel-independent version of ELOOP +var ErrLoop = errors.New("too many symbolic links were encountered in translating the pathname") + // Chroot. This is deprecated, so we don't bother. func (*fsCPIO) Chroot(_ string) (billy.Filesystem, error) { return nil, os.ErrInvalid @@ -400,7 +403,7 @@ func (fs *fsCPIO) resolvelink(filename string) (string, error) { for { var s string if linkcount > 20 { - return "", syscall.ELOOP + return "", ErrLoop } s, err = fs.Readlink(filename) diff --git a/client/union9p.go b/client/union9p.go index 66ebac82..9f38a4e7 100644 --- a/client/union9p.go +++ b/client/union9p.go @@ -19,7 +19,6 @@ import ( "io" "os" "reflect" - "syscall" "github.com/hugelgupf/p9/p9" ) @@ -328,25 +327,25 @@ func (u *union9PFID) UnlinkAt(name string, flags uint32) error { // Mknod implements p9.File.Mknod. func (*union9PFID) Mknod(name string, mode p9.FileMode, major uint32, minor uint32, _ p9.UID, _ p9.GID) (p9.QID, error) { v("union9p:mknod") - return p9.QID{}, syscall.ENOSYS + return p9.QID{}, ErrNosys } // Rename implements p9.File.Rename. func (*union9PFID) Rename(directory p9.File, name string) error { v("union9p:rename") - return syscall.ENOSYS + return ErrNosys } // RenameAt implements p9.File.RenameAt. func (u *union9PFID) RenameAt(oldName string, newDir p9.File, newName string) error { v("union9p:renameat") - return syscall.ENOSYS + return ErrNosys } // StatFS implements p9.File.StatFS. func (*union9PFID) StatFS() (p9.FSStat, error) { v("union9p:statfs") - return p9.FSStat{}, syscall.ENOSYS + return p9.FSStat{}, ErrNosys } // SetAttr implements SetAttr. @@ -397,20 +396,20 @@ func (u *union9PFID) GetAttr(req p9.AttrMask) (p9.QID, p9.AttrMask, p9.Attr, err // SetXattr implements p9.File.SetXattr func (u *union9PFID) SetXattr(attr string, data []byte, flags p9.XattrFlags) error { - return syscall.ENOSYS + return ErrNosys } // ListXattrs implements p9.File.ListXattrs func (u *union9PFID) ListXattrs() ([]string, error) { - return nil, syscall.ENOSYS + return nil, ErrNosys } // GetXattr implements p9.File.GetXattr func (u *union9PFID) GetXattr(attr string) ([]byte, error) { - return nil, syscall.ENOSYS + return nil, ErrNosys } // RemoveXattr implements p9.File.RemoveXattr func (u *union9PFID) RemoveXattr(attr string) error { - return syscall.ENOSYS + return ErrNosys } diff --git a/cmds/cpu/cpu.go b/cmds/cpu/cpu.go index d501f8b6..8c224a33 100644 --- a/cmds/cpu/cpu.go +++ b/cmds/cpu/cpu.go @@ -24,7 +24,6 @@ import ( // We use this ssh because it can unpack password-protected private keys. ossh "golang.org/x/crypto/ssh" - "golang.org/x/sys/unix" ) const defaultPort = "17010" @@ -187,7 +186,7 @@ func newCPU(host string, args ...string) (retErr error) { sigChan := make(chan os.Signal, 1) defer close(sigChan) - signal.Notify(sigChan, unix.SIGINT, unix.SIGTERM) + signal.Notify(sigChan, os.Kill, os.Interrupt) defer signal.Stop(sigChan) errChan := make(chan error, 1) defer close(errChan) @@ -238,9 +237,9 @@ loop: case sig := <-sigChan: var sigErr error switch sig { - case unix.SIGINT: + case os.Interrupt: sigErr = c.Signal(ossh.SIGINT) - case unix.SIGTERM: + case os.Kill: sigErr = c.Signal(ossh.SIGTERM) } if sigErr != nil {