// walkTo handles the walking logic. Walk returns a fidState, len(names) qids // and a nil error if the walk succeeded. If the walk was partially successful, // it returns a nil fidState, less than len(names) qids and a nil error. If the // walk was completely unsuccessful, a nil fidState, nil qid slice and a non-nil // error is returned. func walkTo(oldState *fidState, names []string) (*fidState, []qp.Qid, error) { var ( handle trees.ReadWriteAtCloser isdir, addToLoc bool root, temproot trees.File username, name string newloc FilePath qids []qp.Qid d trees.Dir err error q qp.Qid ) // Walk and Arrived can block, so we don't want to be holding locks. Copy // what we need. oldState.RLock() handle = oldState.handle newloc = oldState.location.Clone() username = oldState.username oldState.RUnlock() root = newloc.Current() if root == nil { return nil, nil, errors.New(InvalidOpOnFid) } if handle != nil { // Can't walk on an open fid. return nil, nil, errors.New(FidOpen) } for i := range names { addToLoc = false name = names[i] switch name { case ".": // This always succeeds, but we don't want to add it to our location // list. case "..": // This also always succeeds, and it either does nothing or shortens // our location list. We don't want anything added to the list // regardless. root = newloc.Parent() if len(newloc) > 1 { newloc = newloc[:len(newloc)-1] } default: // A regular file name. In this case, walking to the name is only // legal if the current file is a directory. addToLoc = true isdir, err = root.IsDir() if err != nil { return nil, nil, err } if !isdir { // Root isn't a dir, so we can't walk. err = errors.New(FidNotDirectory) goto done } d = root.(trees.Dir) if root, err = d.Walk(username, name); err != nil { // The walk failed for some arbitrary reason. goto done } else if root == nil { // The file did not exist err = errors.New(NoSuchFile) goto done } if temproot, err = root.Arrived(username); err != nil { // The Arrived callback failed for some arbitrary reason. goto done } if temproot != nil { root = temproot } } if addToLoc { newloc = append(newloc, root) } q, err = root.Qid() if err != nil { return nil, nil, err } qids = append(qids, q) } done: if err != nil && len(qids) == 0 { return nil, nil, err } if len(qids) < len(names) { return nil, qids, nil } s := &fidState{ username: username, location: newloc, } return s, qids, nil }
func setStat(user string, e trees.File, parent trees.Dir, nstat qp.Stat) error { ostat, err := e.Stat() if err != nil { return err } length := false name := false mode := false owner := false if nstat.Type != ^uint16(0) && nstat.Type != ostat.Type { return errors.New("it is illegal to modify type") } if nstat.Dev != ^uint32(0) && nstat.Dev != ostat.Dev { return errors.New("it is illegal to modify dev") } if nstat.MUID != "" && nstat.MUID != ostat.MUID { return errors.New("it is illegal to modify muid") } if nstat.Atime != ^uint32(0) && nstat.Atime != ostat.Atime { return errors.New("it is illegal to modify atime") } if parent == nil && nstat.Name != "" && nstat.Name != ostat.Name { return errors.New("it is illegal to rename root") } if nstat.Length != ^uint64(0) && nstat.Length != ostat.Length { if ostat.Mode&qp.DMDIR != 0 { return errors.New("cannot set length of directory") } if nstat.Length > ostat.Length { return errors.New("cannot extend length") } length = true } if nstat.Name != "" && nstat.Name != ostat.Name { name = true } if (nstat.UID != "" && nstat.UID != ostat.UID) || (nstat.GID != "" && nstat.GID != ostat.GID) { owner = true } if nstat.Mode != ^qp.FileMode(0) && nstat.Mode != ostat.Mode { mode = true } if nstat.Mtime != ^uint32(0) && nstat.Mtime != ostat.Mtime { // We ignore mtime changes ATM. Only owner can change mtime. } if mode { if err := e.SetMode(user, ostat.Mode&qp.DMDIR | nstat.Mode & ^qp.DMDIR); err != nil { return err } } if owner { if err := e.SetOwner(user, nstat.UID, nstat.GID); err != nil { e.SetMode(user, ostat.Mode) return err } } if name { if err := parent.Rename(user, ostat.Name, nstat.Name); err != nil { e.SetMode(user, ostat.Mode) e.SetOwner(user, ostat.UID, ostat.GID) return err } } if length { if err := e.SetLength(user, nstat.Length); err != nil { e.SetMode(user, ostat.Mode) e.SetOwner(user, ostat.UID, ostat.GID) parent.Rename(user, nstat.Name, ostat.Name) return err } } return nil }
func (fs *FileServer) attach(r *qp.AttachRequest, rs *requestState) { defer fs.handlePanic() if r.Fid == qp.NOFID { fs.sendError(rs, InvalidFid) return } fs.fidLock.Lock() defer fs.fidLock.Unlock() if _, exists := fs.fids[r.Fid]; exists { fs.sendError(rs, FidInUse) return } if fs.AuthFile != nil { if r.AuthFid == qp.NOFID { // There's an authfile, but no authfid was provided. fs.sendError(rs, AuthRequired) return } as, exists := fs.fids[r.AuthFid] if !exists { fs.sendError(rs, UnknownFid) return } if as.handle == nil { fs.sendError(rs, FidNotOpen) return } auther, ok := as.handle.(trees.Authenticator) if !ok { fs.sendError(rs, AfidNotAuthFile) return } authed, err := auther.Authenticated(r.Username, r.Service) if err != nil { fs.sendError(rs, err.Error()) return } if !authed { fs.sendError(rs, PermissionDenied) return } } else if r.AuthFid != qp.NOFID { // There's no authfile, but an authfid was provided. fs.sendError(rs, AuthNotSupported) return } var root trees.File if x, exists := fs.Roots[r.Service]; exists { root = x } else { root = fs.DefaultRoot } if root == nil { fs.sendError(rs, NoSuchService) return } fs.fids[r.Fid] = &fidState{ username: r.Username, location: FilePath{root}, } qid, err := root.Qid() if err != nil { fs.sendError(rs, err.Error()) return } fs.respond(rs, &qp.AttachResponse{ Tag: r.Tag, Qid: qid, }) }