package nfs import ( "bytes" "context" "errors" "os" "github.com/go-git/go-billy/v5" "github.com/willscott/go-nfs-client/nfs/xdr" ) func onSetAttr(ctx context.Context, w *response, userHandle Handler) error { w.errorFmt = wccDataErrorFormatter handle, err := xdr.ReadOpaque(w.req.Body) if err != nil { return &NFSStatusError{NFSStatusInval, err} } fs, path, err := userHandle.FromHandle(ctx, handle) if err != nil { return &NFSStatusError{NFSStatusStale, err} } attrs, err := ReadSetFileAttributes(w.req.Body) if err != nil { return &NFSStatusError{NFSStatusInval, err} } fullPath := fs.Join(path...) info, err := fs.Lstat(ctx, fullPath) if err != nil { if os.IsNotExist(err) { return &NFSStatusError{NFSStatusNoEnt, err} } if errors.Is(err, context.DeadlineExceeded) { return &NFSStatusError{timeoutStatus, err} } return &NFSStatusError{NFSStatusAccess, err} } // see if there's a "guard" if guard, err := xdr.ReadUint32(w.req.Body); err != nil { return &NFSStatusError{NFSStatusInval, err} } else if guard != 0 { // read the ctime. t := FileTime{} if err := xdr.Read(w.req.Body, &t); err != nil { return &NFSStatusError{NFSStatusInval, err} } attr := ToFileAttribute(info, fullPath) if t != attr.Ctime { return &NFSStatusError{NFSStatusNotSync, nil} } } if !CapabilityCheck(fs, billy.WriteCapability) { return &NFSStatusError{NFSStatusROFS, os.ErrPermission} } changer := userHandle.Change(fs) if err := attrs.Apply(ctx, changer, fs, fs.Join(path...)); err != nil { // Already an nfsstatuserror return err } preAttr := ToFileAttribute(info, fullPath).AsCache() writer := bytes.NewBuffer([]byte{}) if err := xdr.Write(writer, uint32(NFSStatusOk)); err != nil { return &NFSStatusError{NFSStatusServerFault, err} } if err := WriteWcc(writer, preAttr, tryStat(ctx, fs, path)); err != nil { return &NFSStatusError{NFSStatusServerFault, err} } if err := w.Write(writer.Bytes()); err != nil { return &NFSStatusError{NFSStatusServerFault, err} } return nil }