home *** CD-ROM | disk | FTP | other *** search
- /* This is all the smarts behind NFS - or at least glue to the real stuff
-
- ©1998, 1999 Joseph Walton
-
- This software is distributed under the terms of the GNU General Public
- License; either version 2 of the License, or (at your option) any
- later version.
- */
-
- /*
- 30-Nov-1999 - Major changes to support new RPC code.
- */
-
- #include "nfsd.h"
- #include "memory.h"
- #include "nfs_utils.h"
- #include "handle_list.h"
- #include "auth.h"
- #include <string.h>
- #include <stdio.h>
-
- #include <dos/dos.h>
- #include <proto/dos.h>
-
- int32 *fillinfibattrs(int32 *rptr, struct LocalFile *lf);
- nfsstat set_attrs(struct LocalFile *lf, struct sattr *sa, BPTR handle);
- BOOL SeekWithExtend(BPTR handle, LONG offset);
-
- extern struct InfoData infodata;
- extern struct FileInfoBlock fib;
-
- #define HEADER_INT32S (6)
- #define FATTR_INT32S (11 + (3 * 2))
- #define FHANDLE_INT32S (INT32S(NFS_FHSIZE))
-
- #define DIROPRES_INT32S (HEADER_INT32S + 1 + FHANDLE_INT32S + FATTR_INT32S)
-
- #define ISERR(x) ((x) != NFS_OK)
- #define PERR(c,s,x) VERBOSE(print_auth_once(c); printf(s " failed - %s\n", reason(x)))
- #define PRES(c,s,x) if(ISERR(x)){PERR(c,s,x)}
-
- int nfsproc_getattr_2_svc(nfs_fh *handle, struct Call *call, int32 *rptr)
- {
- DEBUG(print_auth_once(call));
- DEBUG(puts("nfsproc_getattr_2_svc"));
-
- /* Strictly, Unix would usually require only read access to the
- parent directory here. However, that doesn't make much sense
- under AmigaDOS. It's all a little arbitrary at the moment,
- but it sort of makes sense.
- */
-
- nfsstat status;
- struct LocalFile *lf;
-
- if (lf = get_by_handle(handle, &status, ACCESS_READ)) {
- DEBUG(printf("Getting status of '%s'\n", lf->lf_Name));
-
- status = check_permission(call, lf, ALLOW_GETATTRS);
-
- if (status == NFS_OK) {
- *rptr++ = P_ENUM(NFS_OK);
- fillinfibattrs(rptr, lf);
- } else {
- print_auth_once(call);
- printf("Not authorised to get attributes of '%s' - %s\n", lf->lf_Name,
- reason(status));
- }
-
- release_lf(lf);
- } else {
- PERR(call, "getattr", status);
- }
-
- if (status == NFS_OK) {
- return HEADER_INT32S + 1 + FATTR_INT32S;
- } else {
- *rptr = P_ENUM(status);
- return HEADER_INT32S + 1;
- }
- }
-
- /* This isn't quite atomic at the moment, although this is only
- tickled in a couple of cases */
- int nfsproc_setattr_2_svc(struct sattrargs *args, struct Call *call, int32 *rptr)
- {
- DEBUG(print_auth_once(call));
- DEBUG(puts("nfsproc_setattr_2_svc"));
-
- nfsstat status;
-
- struct LocalFile *lf;
- if (lf = get_by_handle(args->handle, &status, ACCESS_READ)) {
-
- /* Are they allowed to set the attributes? */
- status = check_permission(call, lf, ALLOW_SETATTRS);
-
- if (status == NFS_OK) {
- status = set_attrs(lf, &args->sattr, (BPTR)NULL);
-
- if (status == NFS_OK) {
- *rptr++ = P_ENUM(NFS_OK);
- fillinfibattrs(rptr, lf);
- } else {
- print_auth_once(call);
- printf("Failed to set attributes of '%s' - %s\n",
- lf->lf_Name, reason(status));
- }
- } else {
- print_auth_once(call);
- printf("Attribute setting of '%s' not permitted", lf->lf_Name);
- }
-
- release_lf(lf);
- }
-
- if (status != NFS_OK) {
- *rptr = P_ENUM(status);
- PERR(call, "setattr", status);
- return HEADER_INT32S + 1;
- }
-
- return HEADER_INT32S + 1 + FATTR_INT32S;
- }
-
- int nfsproc_lookup_2_svc(struct diropargs *argp, struct Call *call, int32 *rptr)
- {
- DEBUG(print_auth_once(call));
- DEBUG(puts("nfsproc_lookup_2_svc"));
-
- /* They must have read access to the parent directory,
- as well as to the file being looked up */
-
- nfsstat status;
- struct LocalFile *dir_lf;
-
- if (dir_lf = get_by_handle(argp->dir_handle, &status, ACCESS_READ))
- {
- status = check_permission(call, dir_lf, ALLOW_READ);
-
- if (status == NFS_OK) {
- STRPTR name = argp->filename;
-
- /* A few special cases... */
- if (!strcmp(name, ".") || (*name == '\0')) { // "." or ""
- /* They're looking up the directory itself */
- *rptr++ = P_ENUM(NFS_OK);
- rptr = fh_copy(dir_lf, rptr);
- fillinfibattrs(rptr, dir_lf);
- } else {
- STRPTR full_name;
- /* Frankly this should be handled locally. Some clients
- don't see it quite the same way, however, so I suppose
- we'd better deal with it. */
- if (!strcmp(name, "..")) {
- /* They want the parent of this directory */
- full_name = DupString(dir_lf->lf_Name);
-
- if (full_name) {
- *PathPart(full_name) = 0; /* Can this *ever* return NULL? */
- DEBUG(printf("Parent of '%s' is '%s'\n",
- dir_lf->lf_Name, full_name));
- } else {
- status = NFSERR_NOENT;
- print_auth_once(call);
- printf("Problem getting parent of '%s'\n",
- dir_lf->lf_Name);
- }
- } else {
- full_name = name_of_diropargs(argp->filename, dir_lf, &status);
- }
-
- /* Did we get anything? */
-
- if (full_name) {
- struct LocalFile *nlf = get_namedlocalfile(full_name);
-
- if (nlf) {
- BPTR lock = Lock(full_name, ACCESS_READ);
- if (lock) {
- if (Examine(lock, &fib)) { /* Everything seems to have worked! */
- correct_struct(nlf); /* Give it an inode etc. */
-
- /* Can they actually read that file? If
- not, then they don't get it */
-
- status = check_permission(call,
- nlf, ALLOW_READ);
-
- if (status == NFS_OK) {
- *rptr++ = P_ENUM(NFS_OK);
- rptr = fh_copy(nlf, rptr);
- fillinfibattrs(rptr, nlf);
- } else {
- print_auth_once(call);
- printf("Forbidden from looking up '%s' - %s\n", full_name, reason(status));
- }
- } else
- status = NFS_err(IoErr());
-
- UnLock(lock);
- } else
- status = NFS_wt_err(NFSERR_NOTDIR);
- } else
- status = NFSERR_IO; /* No memory? */
-
- FreeVecPooled(full_name);
- }
- }
- }
-
- release_lf(dir_lf);
- }
-
- if (status != NFS_OK) {
- *rptr = P_ENUM(status);
- PERR(call, "lookup", status);
- return HEADER_INT32S + 1;
- }
-
- return DIROPRES_INT32S;
- }
-
- int nfsproc_read_2_svc(struct readargs *argp, struct Call *call, int32 *rptr,
- int32 *rlimit)
- {
- struct LocalFile *lf;
- int len;
- BPTR handle;
-
- DEBUG(print_auth_once(call));
- DEBUG(puts("nfsproc_read_2_svc"));
-
- nfsstat status;
- int count = HEADER_INT32S + 1;
-
- /* How odd - I'm using a completely different structure for
- this function. Odd. */
-
- lf = get_by_handle(argp->handle, &status, ACCESS_READ);
-
- if (!lf) { /* Some kind of error */
- *rptr = P_ENUM(status);
- return HEADER_INT32S + 1;
- }
-
- /* Obviously they need to have permission here... */
- status = check_permission(call, lf, ALLOW_READ);
-
- if (status != NFS_OK) {
- *rptr = P_ENUM(status);
- return HEADER_INT32S + 1;
- }
-
- if (verbose) {
- print_auth_once(call);
- printf("%s: Asked to read %d bytes from %d\n",
- lf->lf_Name, argp->count, argp->offset);
- }
-
- len = argp->count;
- if (len > NFS_MAXDATA)
- len = NFS_MAXDATA;
-
- if (handle = OpenFromLock(lf->lf_Lock)) {
- lf->lf_Lock = NULL; /* It's no longer valid */
-
- if (Seek(handle, argp->offset, OFFSET_BEGINNING) != -1) {
- int32 *buffer = rptr + 1 + FATTR_INT32S + 1;
-
- /* This will be NFS_MAXDATA, but let's check anyway */
- int buf_len = (rlimit - buffer) * SINT32;
- if (len > buf_len) {
- len = buf_len;
- }
-
- len = Read(handle, buffer, len);
- if (len == -1) { /* Error */
- LONG e = IoErr();
- if (verbose) {
- print_auth_once(call);
- PrintFault(e, "Unable to read from file");
- }
- status = NFS_err(e);
- } else {
- /* We succeeded */
- *rptr++ = P_ENUM(NFS_OK);
- rptr = fillinfibattrs(rptr, lf); /* The old fib won't have been changed */
- *rptr = P_UINT(len);
-
- count = HEADER_INT32S + 1 + FATTR_INT32S + 1 + INT32S(len);
- }
- } else {
- status = NFS_err(IoErr()); /* That's an error, I'm afraid */
- }
-
- Close(handle);
- } else
- status = NFS_wt_err(NFSERR_ISDIR);
-
- release_lf(lf);
-
- if (status != NFS_OK) {
- *rptr = P_ENUM(status);
- PERR(call, "read", status);
- }
-
- return count;
- }
-
- int nfsproc_write_2_svc(struct writeargs *argp, struct Call *call, int32 *rptr)
- {
- DEBUG(print_auth_once(call));
- DEBUG(puts("nfsproc_write_2_svc"));
-
- /* Obviously, they must have write access to this file */
-
- nfsstat status;
- struct LocalFile *lf;
-
- if (lf = get_by_handle(argp->handle, &status, ACCESS_WRITE)) {
- /* Basic debugging output */
- VERBOSE(printf("'%s' - writing %d bytes at %d\n",
- lf->lf_Name, argp->count, argp->offset));
-
- status = check_permission(call, lf, ALLOW_WRITE);
-
- if (status == NFS_OK) {
- BPTR handle;
- if (handle = OpenFromLock(lf->lf_Lock)) {
- lf->lf_Lock = NULL;
-
- /* Since seeking in the Right Way involves some work
- it's moved to its own function */
- if (SeekWithExtend(handle, argp->offset) &&
- (Write(handle, argp->data, argp->count) == argp->count))
- {
- if (ExamineFH(handle, &fib)) {
- /* Success */
- *rptr++ = P_ENUM(NFS_OK);
- fillinfibattrs(rptr, lf);
- } else {
- status = NFS_err(IoErr());
- }
- } else {
- status = NFS_err(IoErr());
- }
-
- Close(handle);
- } else
- status = NFS_wt_err(NFSERR_ISDIR);
- }
-
- release_lf(lf);
- }
-
- if (status != NFS_OK) {
- *rptr = P_ENUM(status);
- PERR(call, "write", status);
- return HEADER_INT32S + 1;
- }
-
- return HEADER_INT32S + 1 + FATTR_INT32S;
- }
-
- /* When a creation request comes in, attribute it if it is not
- explicitly claiming ownership */
- static void impose_auth(struct Call *call, struct sattr *sa)
- {
- if (!call->c_auth.isUnix)
- return;
-
- if (sa->uid == -1)
- sa->uid = call->c_auth.uid;
-
- if (sa->gid == -1)
- sa->gid = call->c_auth.gid;
- }
-
- int nfsproc_create_2_svc(struct createargs *argp, struct Call *call, int32 *rptr)
- {
- struct LocalFile *dir_lf;
-
- DEBUG(print_auth_once(call));
- DEBUG(puts("nfsproc_create_2_svc"));
-
- /* We must have write access to the directory,
- and there must not be an existing entity of that name */
-
- nfsstat status;
-
- if (dir_lf = get_by_handle(argp->where.dir_handle,
- &status, ACCESS_READ)) {
-
- status = check_permission(call, dir_lf, ALLOW_WRITE);
-
- if (status == NFS_OK) {
- STRPTR full_name;
-
- if (full_name = name_of_diropargs(argp->where.filename, dir_lf,
- &status))
- {
- struct LocalFile *nlf;
-
- print_auth_once(call);
- printf("Creating file '%s'\n", full_name);
-
- nlf = get_namedlocalfile(full_name);
-
- /* There is room for a race condition here, in which we
- delete a file just created by another process */
-
- if (nlf->lf_Type == NFNON) { /* It doesn't already exist */
- BPTR handle;
- if (handle = Open(full_name, MODE_NEWFILE)) {
- /* To set things, we need a non-exclusive handle */
- if (ChangeMode(CHANGE_FH, handle, MODE_READWRITE)) {
- ExamineFH(handle, &fib);
- correct_struct(nlf); /* Give it an inode */
-
- impose_auth(call, &(argp->sattr));
- status = set_attrs(nlf, &(argp->sattr), handle);
-
- if (status == NFS_OK) {
- /* Succeeded */
- *rptr++ = P_ENUM(NFS_OK);
- rptr = fh_copy(nlf, rptr);
- rptr = fillinfibattrs(rptr, nlf);
- DEBUG(printf("Created '%s'\n", full_name));
- } else {
- printf("Unable to set attributes of new file - %s", reason(status));
- }
- } else {
- PrintFault(IoErr(), "Unable to change a read-only handle into a read-write one");
- status = NFS_err(IoErr());
- }
- Close(handle);
- } else {
- PrintFault(IoErr(), "Unable to open new file");
- status = NFS_err(IoErr());
- }
- } else {
- printf("File already exists - type is %d\n", nlf->lf_Type);
- status = NFSERR_EXIST;
- }
-
- FreeVecPooled(full_name);
- }
- }
- release_lf(dir_lf);
- }
-
- if (status != NFS_OK) {
- *rptr = P_ENUM(status);
- PERR(call, "create", status);
- return HEADER_INT32S + 1;
- }
-
- return DIROPRES_INT32S;
- }
-
-
- /* This will delete files and empty directories */
- static nfsstat generic_remove(struct diropargs *argp, struct Call *call)
- {
- nfsstat status;
- struct LocalFile *dir_lf;
-
- /* They only need write access to the parent directory, not access
- to the actual file in question. UNIX has the notion of the 'sticky bit'
- to get around this, and require users to own files they want to delete,
- but we don't consider that here.
- */
-
- if (dir_lf = get_by_handle(argp->dir_handle, &status, ACCESS_READ))
- {
- /* They must be able to write to this directory */
- status = check_permission(call, dir_lf, ALLOW_WRITE);
-
- if (status == NFS_OK) {
- STRPTR full_name = name_of_diropargs(argp->filename, dir_lf, &status);
-
- if (full_name) {
- print_auth_once(call);
- printf("Deleting '%s'\n", full_name);
-
- /* Remove any trace */
- if (DeleteFile(full_name)) {
- lose_localfile(full_name);
- /* We succeeded - status is already NFS_OK */
- } else {
- status = NFS_wt_err(NFSERR_NOTDIR);
- }
-
- FreeVecPooled(full_name);
- }
- }
- release_lf(dir_lf);
- }
-
- return status;
- }
-
- nfsstat nfsproc_remove_2_svc(struct diropargs *argp, struct Call *call)
- {
- DEBUG(print_auth_once(call));
- DEBUG(puts("nfsproc_remove_2_svc"));
-
- /* Note that this will delete directories as well as files -
- I do not consider this a security hole */
-
- nfsstat result = generic_remove(argp, call);
-
- PRES(call, "remove", result);
- return result;
- }
-
- nfsstat nfsproc_rmdir_2_svc(struct diropargs *argp, struct Call *call)
- {
- DEBUG(print_auth_once(call));
- DEBUG(puts("nfsproc_rmdir_2_svc"));
-
- /* Note that this will delete files as well as directories -
- I do not consider this a security hole */
-
- nfsstat result = generic_remove(argp, call);
-
- PRES(call, "rmdir", result);
- return result;
- }
-
-
- nfsstat nfsproc_rename_2_svc(struct renameargs *argp, struct Call *call)
- {
- nfsstat result;
- struct LocalFile *lf_orig, *lf_new;
-
- DEBUG(print_auth_once(call));
- DEBUG(puts("nfsproc_rename_2_svc"));
-
- /* Security: We must have write access to both the directories
- involved */
-
- /* Note that write access to the file itself is *not* required */
-
- if (lf_orig = get_by_handle(argp->from.dir_handle, &result,
- ACCESS_READ)) {
-
- result = check_permission(call, lf_orig, ALLOW_WRITE);
-
- if (result == NFS_OK) {
- if (lf_new = get_by_handle(argp->to.dir_handle, &result,
- ACCESS_READ)) {
-
- result = check_permission(call, lf_new, ALLOW_WRITE);
-
- if (result == NFS_OK) {
- STRPTR source_name =
- name_of_diropargs(argp->from.filename, lf_orig, &result);
-
- if (source_name) {
- STRPTR dest_name =
- name_of_diropargs(argp->to.filename, lf_new, &result);
-
- if (dest_name) {
- print_auth_once(call);
- printf("Renaming '%s' to '%s'\n", source_name, dest_name);
-
- if (Rename(source_name, dest_name)) {
- /* Lose any reference to the old name */
- lose_localfile(source_name);
- result = NFS_OK;
- } else
- result = NFS_err(IoErr());
-
- FreeVecPooled(dest_name);
- }
- FreeVecPooled(source_name);
- }
- }
-
- release_lf(lf_new);
- }
- }
-
- release_lf(lf_orig);
- }
-
- PRES(call, "rename", result);
- return result;
- }
-
- int nfsproc_mkdir_2_svc(struct createargs *argp, struct Call *call, int32 *rptr)
- {
- DEBUG(print_auth_once(call));
- DEBUG(puts("nfsproc_mkdir_2_svc"));
-
- /* Security - the user must have write access to the parent
- directory */
-
- nfsstat status;
- struct LocalFile *dir_lf;
-
- if (dir_lf = get_by_handle(argp->where.dir_handle,
- &status, ACCESS_READ)) {
-
- status = check_permission(call, dir_lf, ALLOW_WRITE);
-
- if (status == NFS_OK) {
- STRPTR full_name = name_of_diropargs(argp->where.filename,
- dir_lf, &status);
-
- if (full_name) {
- struct LocalFile *nlf;
-
- print_auth_once(call);
- printf("Making directory '%s'\n", full_name);
-
- if (nlf = get_namedlocalfile(full_name)) {
- if (nlf->lf_Lock = CreateDir(full_name) ) {
- /* We can't do much with an exclusive lock on it,
- so turn it into a shared one */
-
- if (ChangeMode(CHANGE_LOCK, nlf->lf_Lock, ACCESS_READ)) {
- if (Examine(nlf->lf_Lock, &fib)) {
- correct_struct(nlf);
-
- /* Set the attributes */
- impose_auth(call, &(argp->sattr));
- status =
- set_attrs(nlf, &(argp->sattr), (BPTR)NULL);
-
- if (status == NFS_OK) {
- *rptr++ = P_ENUM(NFS_OK);
- rptr = fh_copy(nlf, rptr);
- fillinfibattrs(rptr, nlf);
- }
-
- } else {
- PrintFault(IoErr(), "Unable to Examine() new directory");
- status = NFS_err(IoErr());
- }
- } else {
- PrintFault(IoErr(), "Could not change exclusive lock to shared lock on new directory");
- status = NFS_err(IoErr());
- }
-
- UnLock(nlf->lf_Lock);
- nlf->lf_Lock = NULL;
- } else {
- PrintFault(IoErr(), "Could not create directory");
- status = NFS_err(IoErr());
- }
- } else
- status = NFS_err(IoErr());
-
- FreeVecPooled(full_name);
- }
- }
- }
-
- if (status != NFS_OK) {
- *rptr = P_ENUM(status);
- PERR(call, "mkdir", status);
- return HEADER_INT32S + 1;
- }
-
- return DIROPRES_INT32S;
- }
-
-
- int nfsproc_readdir(struct readdirargs *args, struct Call *call,
- int32 *rptr, int32 *rlimit)
- {
- struct LocalFile *dir_lf;
-
- DEBUG(print_auth_once(call));
- DEBUG(puts("nfsproc_readdir_2_svc"));
-
- nfsstat status;
- /* They need read access to the directory */
-
- int count;
-
- if (dir_lf = get_by_handle(args->dir_handle, &status, ACCESS_READ)) {
-
- status = check_permission(call, dir_lf, ALLOW_READ);
-
- if (status == NFS_OK) {
- count = HEADER_INT32S + readdir(dir_lf, rptr, rlimit, args->cookie, args->count);
- } else {
- *rptr = P_ENUM(status);
- count = 7;
- }
- release_lf(dir_lf);
- } else {
- *rptr = P_ENUM(status);
- count = 7;
- }
-
- PRES(call, "readdir", status);
-
- return count;
- }
-
- int nfsproc_statfs_2_svc(nfs_fh *handle, struct Call *call, int32 *rptr)
- {
- DEBUG(print_auth_once(call));
- DEBUG(puts("nfsproc_statfs_2_svc"));
-
- int count;
- nfsstat status;
- struct LocalFile *lf;
-
- if (lf = get_by_handle(handle, &status, ACCESS_READ)) {
- status = check_permission(call, lf, ALLOW_READ);
-
- if (status == NFS_OK) {
- VERBOSE(printf("BPB %ld; NB %ld; BU %ld\n",
- infodata.id_BytesPerBlock, infodata.id_NumBlocks,
- infodata.id_NumBlocksUsed));
-
- *rptr++ = P_ENUM(NFS_OK);
-
- *rptr++ = P_UINT(NFS_MAXDATA); // preferred transfer size in bytes
- *rptr++ = P_UINT(infodata.id_BytesPerBlock); // fundamental file system block size
- *rptr++ = P_UINT(infodata.id_NumBlocks); // total blocks in file system
-
- /* Free blocks in filing system */
- *rptr++ = P_UINT(infodata.id_NumBlocks - infodata.id_NumBlocksUsed);
-
- /* Free blocks available to non-superuser (the same under AmigaOS */
- *rptr = P_UINT(infodata.id_NumBlocks - infodata.id_NumBlocksUsed);
-
- count = 6 + 6;
- } else {
- *rptr = P_ENUM(status);
- count = 7;
- }
-
- release_lf(lf);
- } else {
- *rptr = P_ENUM(status);
- count = 7;
- }
-
- PRES(call, "statfs", status);
-
- return count;
- }
-
-
- /* These are private support functions */
-
- static int32 *fillinfibattrs(int32 *rptr, struct LocalFile *lf)
- {
- u_int mode;
-
- /* What type is it? */
- switch (*rptr++ = P_ENUM(NFS_type(fib.fib_DirEntryType))) {
- /* V2 mandates this nasty feature - we need to encode the filetype
- in the mode as well */
- case NFDIR:
- mode = NFSMODE_DIR;
- break;
-
- case NFREG:
- mode = NFSMODE_REG;
- break;
-
- case NFLNK:
- mode = NFSMODE_LNK;
- break;
-
- default:
- mode = 0;
- break;
- }
-
- /* Set the protection bits here... */
- mode |= NFS_mode((lf->lf_ForcedProtection != -1)
- ? lf->lf_ForcedProtection
- : fib.fib_Protection);
-
- *rptr++ = P_UINT(mode);
-
- /* # hard links - may confuse Unix programs for directories */
- *rptr++ = P_UINT(1);
-
- /* Do any UID/GID mappings here */
- *rptr++ = P_UINT((lf->lf_ForcedUID != -1) ? lf->lf_ForcedUID : fib.fib_OwnerUID);
- *rptr++ = P_UINT((lf->lf_ForcedGID != -1) ? lf->lf_ForcedGID : fib.fib_OwnerGID);
-
- *rptr++ = P_UINT(fib.fib_Size); // file size in bytes
- *rptr++ = P_UINT(infodata.id_BytesPerBlock); // preferred block size
- *rptr++ = P_UINT(0); // special device # (we ignore this)
- *rptr++ = P_UINT(fib.fib_NumBlocks); // Kb of disk used by file
- *rptr++ = P_UINT(0); // device # - pointless unless we allow re-exports
-
- if ((*rptr++ = lf->lf_FileID) <= 0) /* Should never happen */
- printf("ERROR: Gave out inode %d for %s\n",
- lf->lf_FileID, lf->lf_Name);
-
- /* We say that the file was creating, modified *and* used at the same
- time - namely, the real last modification time */
- struct nfstime ntime;
- Unix_date(&(fib.fib_Date), &ntime);
-
- /* NFS uses unsigned values here, so we can't refer to anything before
- 1970. In fact, we're not checking that at the moment, so
- problems would result if somebody tried it. */
-
- *rptr++ = P_UINT(ntime.seconds); // time of last access
- *rptr++ = P_UINT(ntime.useconds);
- *rptr++ = P_UINT(ntime.seconds); // time of last modification
- *rptr++ = P_UINT(ntime.useconds);
- *rptr++ = P_UINT(ntime.seconds); // time of last status change
- *rptr++ = P_UINT(ntime.useconds);
-
- return rptr;
- }
-
- /* Try and set the attributes of a file, taking a locked LocalFile structure
- which may or may not be locked on return.
- If one attribute is not properly set,
- it will continue and try and set the others. You may consider this
- to be wrong.
- It assumes the file was Examine()'d beforehand, and will leave it
- with an up-to-date examination afterwards.
- You may optionally pass an open file handle which will be used.
- */
- static nfsstat set_attrs(struct LocalFile *lf, struct sattr *sa, BPTR handle)
- {
- nfsstat status = NFS_OK;
-
- VERBOSE(printf("'%s' - desired attributes: %o %d %d %d (%d %d) (%d %d)\n",
- lf->lf_Name,
- sa->mode, sa->uid, sa->gid, sa->size,
- sa->atime.seconds, sa->atime.useconds,
- sa->mtime.seconds, sa->mtime.useconds));
-
- /* Only some attributes may need setting */
- #define ISSET(x) ((x) != -1)
-
- /* Here we only change the protection bits we know about.
- This means delete gets left alone. */
- if (ISSET(sa->mode)) {
- #define MASK (FIBF_READ | FIBF_WRITE | FIBF_EXECUTE \
- | FIBF_GRP_READ | FIBF_GRP_WRITE \
- | FIBF_GRP_EXECUTE | FIBF_OTR_READ \
- | FIBF_OTR_WRITE | FIBF_OTR_EXECUTE)
-
- LONG prot = Amiga_prot(sa->mode);
- prot &= MASK;
- prot |= (fib.fib_Protection & (~MASK));
-
- /* That is, we take the old values for things we don't
- concern ourselves with */
-
- if (!SetProtection(lf->lf_Name, prot)) {
- PrintFault(IoErr(), "Unable to set protection bits");
- status = NFS_err(IoErr());
- }
-
- #undef MASK
- }
-
- /* There has to be a better way of doing this bit... */
- if (ISSET(sa->uid) || ISSET(sa->gid)) {
- LONG owner = (fib.fib_OwnerUID << 16) | fib.fib_OwnerGID;
-
- if (ISSET(sa->uid))
- owner = ((owner & 0x0000ffff) | (sa->uid << 16));
-
- if (ISSET(sa->gid))
- owner = ((owner & 0xffff0000) | sa->gid);
-
- /* The ramdisk doesn't handle this in v39 */
- if (!SetOwner(lf->lf_Name, owner)) {
- PrintFault(IoErr(), "Unable to set ownership");
- status = NFS_err(IoErr());
- }
- }
-
- /* Let's sort out the dates... */
-
- /* We simply ignore atime - the Amiga has no concept of it,
- and any kludgy use runs the risk of creating subtle problems */
-
- struct nfstime *mt = &(sa->mtime);
- if (ISSET(mt->seconds)) {
- if (!ISSET(mt->useconds)) {
- mt->useconds = 0;
- }
-
- struct DateStamp ds;
- Amiga_date(mt, &ds);
- if (!SetFileDate(lf->lf_Name, &ds) && (status == NFS_OK)) {
- PrintFault(IoErr(), "Unable to set date");
- status = NFS_err(IoErr());
- }
- }
-
- /* Set the size - we may need to open the file */
- if (ISSET(sa->size)) {
- BOOL we_opened = FALSE;
- if (!handle) {
- we_opened = TRUE;
- if (handle = OpenFromLock(lf->lf_Lock)) {
- lf->lf_Lock = NULL;
-
- /* We possibly need to change the mode of the lock */
- ChangeMode(CHANGE_FH, handle, MODE_READWRITE);
-
- /* If there's an error changing the mode, it will be
- reported later when we try and write */
- }
- }
-
- if (handle) {
- if (-1 == SetFileSize(handle, sa->size, OFFSET_BEGINNING)) {
- PrintFault(IoErr(), "Unable to set file size");
- status = NFS_err(IoErr());
- }
- if (!ExamineFH(handle, &fib)) {
- PrintFault(IoErr(), "Unable to examine open file");
- status = NFS_err(IoErr());
- }
- if (we_opened)
- Close(handle);
- } else {
- /* Hmmm... is it an error to set the size of a directory? */
- PrintFault(IoErr(), "Couldn't open file to set its length");
- status = NFS_wt_err(NFSERR_ISDIR);
- }
- }
-
- #undef ISSET
-
- /* Examine the lock if we still have it, otherwise trust that
- our last examination was correct */
-
- if (status == NFS_OK) {
- if (lf->lf_Lock && !Examine(lf->lf_Lock, &fib))
- return NFS_err(IoErr());
- }
-
- return status;
- }
-
-
- /* Seek in a file, extending it if necessary */
- static BOOL SeekWithExtend(BPTR handle, LONG offset)
- {
- if (Seek(handle, offset, OFFSET_BEGINNING) != -1) {
- return TRUE; /* It worked fine */
- }
-
- /* If the write position is past the current EOF then
- we need to extend it ourselves - DOS won't do it.
- Thanks to Henryk Richter
- */
-
- if (Seek(handle, 0, OFFSET_END) == -1) {
- return FALSE;
- }
-
- LONG position = Seek(handle, 0, OFFSET_CURRENT);
-
- /* Do we need to extend it? */
- if (position < offset) {
- if (offset != SetFileSize(handle, offset, OFFSET_BEGINNING))
- return FALSE;
-
- Seek(handle, offset, OFFSET_BEGINNING);
- position = Seek(handle, 0, OFFSET_CURRENT);
- }
-
- return (position == offset);
- }
-
-