/* * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights * Reserved. This file contains Original Code and/or Modifications of * Original Code as defined in and that are subject to the Apple Public * Source License Version 1.0 (the 'License'). You may not use this file * except in compliance with the License. Please obtain a copy of the * License at http://www.apple.com/publicsource and read it before using * this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License." * * @APPLE_LICENSE_HEADER_END@ */ #include #include #import #import #import #import #import #import "AMString.h" #import "automount.h" #import "NFSHeaders.h" #import "Controller.h" #import "AMMap.h" #import "log.h" #import "AMVnode.h" #import "Server.h" extern char *gNoLookupTarget; extern char *gLookupTarget; static char gNFSReaddir[] = "[ NFS READDIR ]"; /* Some essential cheats: */ struct svcudp_data { u_int su_iosz; /* byte size of send.recv buffer */ u_long su_xid; /* transaction id */ XDR su_xdrs; /* XDR handle */ char su_verfbody[MAX_AUTH_BYTES]; /* verifier body */ char * su_cache; /* cached data, NULL if no cache */ }; #define su_data(xprt) ((struct svcudp_data *)(xprt->xp_p2)) u_long rpc_xid; /* copy of su_xid, derived from rm_xid */ #ifndef __APPLE__ #define nfsproc_create_2_svc nfsproc_create_2 #define nfsproc_getattr_2_svc nfsproc_getattr_2 #define nfsproc_link_2_svc nfsproc_link_2 #define nfsproc_lookup_2_svc nfsproc_lookup_2 #define nfsproc_mkdir_2_svc nfsproc_mkdir_2 #define nfsproc_null_2_svc nfsproc_null_2 #define nfsproc_read_2_svc nfsproc_read_2 #define nfsproc_readdir_2_svc nfsproc_readdir_2 #define nfsproc_readlink_2_svc nfsproc_readlink_2 #define nfsproc_remove_2_svc nfsproc_remove_2 #define nfsproc_rename_2_svc nfsproc_rename_2 #define nfsproc_rmdir_2_svc nfsproc_rmdir_2 #define nfsproc_root_2_svc nfsproc_root_2 #define nfsproc_setattr_2_svc nfsproc_setattr_2 #define nfsproc_statfs_2_svc nfsproc_statfs_2 #define nfsproc_symlink_2_svc nfsproc_symlink_2 #define nfsproc_write_2_svc nfsproc_write_2 #define nfsproc_writecache_2_svc nfsproc_writecache_2 #endif int _rpcpmstart; /* Started by a port monitor ? */ int _rpcfdtype; /* Whether Stream or Datagram ? */ int _rpcsvcdirty; /* Still serving ? */ extern void send_pid_to_parent(void); extern int doing_timeout; extern int debug; extern int debug_proc; Vnode *new_mount_dir; #import struct debug_file_handle { unsigned int i[8]; }; char * fhtoc(nfs_fh *fh) { static char str[32]; struct debug_file_handle *dfh; dfh = (struct debug_file_handle *)fh; sprintf(str, "%u", dfh->i[0]); return str; } /* * add up sizeof (valid + fileid + name + cookie) - strlen(name) */ #define ENTRYSIZE (3 * BYTES_PER_XDR_UNIT + NFS_COOKIESIZE) /* * sizeof(status + eof) */ #define JUNKSIZE (2 * BYTES_PER_XDR_UNIT) attrstat * nfsproc_getattr_2_svc(nfs_fh *fh, struct svc_req *req) { static attrstat astat; struct file_handle *ifh; Vnode *n; char mtimestring[FORMATTED_TIME_LEN]; ifh = (struct file_handle *)fh; sys_msg(debug_proc, LOG_DEBUG, "-> getattr"); sys_msg(debug_proc, LOG_DEBUG, " fh = %s", fhtoc(fh)); astat.status = NFS_OK; n = [controller vnodeWithID:ifh->node_id]; if (n == nil) { sys_msg(debug, LOG_WARNING, "getattr for non-existent file handle %s", fhtoc(fh)); astat.status = NFSERR_NOENT; } if (astat.status != NFS_OK) { sys_msg(debug_proc, LOG_DEBUG, "<- getattr (error %d)", astat.status); return(&astat); } sys_msg(debug_proc, LOG_DEBUG, " name = %s", [[n name] value]); astat.attrstat_u.attributes = [n attributes]; sys_msg(debug_proc, LOG_DEBUG, " mtime = %s", formattimevalue(&astat.attrstat_u.attributes.mtime, mtimestring, sizeof(mtimestring))); if ((astat.attrstat_u.attributes.mode & S_IFMT) == S_IFLNK) { sys_msg(debug_proc, LOG_DEBUG, " (Link flags: %s; %s,%s)", (astat.attrstat_u.attributes.mode & S_ISVTX) ? "trigger" : "not trigger", (astat.attrstat_u.attributes.mode & S_ISUID) ? "mounted" : "not mounted", (astat.attrstat_u.attributes.mode & S_ISGID) ? "needs auth." : "no auth. needed"); } sys_msg(debug_proc, LOG_DEBUG, "<- getattr"); return(&astat); } /* Does something */ diropres * nfsproc_lookup_2_svc(diropargs *args, struct svc_req *req) { static diropres res; struct file_handle *ifh; Vnode *n; String *s; char mtimestring[FORMATTED_TIME_LEN]; ifh = (struct file_handle *)&(args->dir); sys_msg(debug_proc, LOG_DEBUG, "-> lookup"); if (req->rq_cred.oa_flavor == AUTH_UNIX) { struct authunix_parms *aup; uid_t uid; aup = (struct authunix_parms *)req->rq_clntcred; uid = aup->aup_uid; sys_msg(debug_proc, LOG_DEBUG, " requesting uid = %ld", uid); } else { sys_msg(debug_proc, LOG_DEBUG, " requesting uid = unknown"); } sys_msg(debug_proc, LOG_DEBUG, " dir fh = %s", fhtoc(&(args->dir))); sys_msg(debug_proc, LOG_DEBUG, " file = %s", args->name); res.status = NFS_OK; gLookupTarget = args->name; n = [controller vnodeWithID:ifh->node_id]; if (n == nil) { sys_msg(debug, LOG_WARNING, "lookup for non-existent file handle %s", fhtoc(&(args->dir))); res.status = NFSERR_NOENT; } if (res.status != NFS_OK) { sys_msg(debug_proc, LOG_DEBUG, "<- lookup (error %d)", res.status); gLookupTarget = gNoLookupTarget; return(&res); } s = [String uniqueString:args->name]; n = [n lookup:s]; [s release]; if (n == nil) { res.status = NFSERR_NOENT; sys_msg(debug_proc, LOG_DEBUG, "<- lookup (res=%d)", res.status); gLookupTarget = gNoLookupTarget; return(&res); } [n getFileHandle:(nfs_fh *)&res.diropres_u.diropres.file]; sys_msg(debug_proc, LOG_DEBUG, " return fh = %s", fhtoc(&res.diropres_u.diropres.file)); res.diropres_u.diropres.attributes = [n attributes]; sys_msg(debug_proc, LOG_DEBUG, " mtime = %s", formattimevalue(&res.diropres_u.diropres.attributes.mtime, mtimestring, sizeof(mtimestring))); if ((res.diropres_u.diropres.attributes.mode & S_IFMT) == S_IFLNK) { sys_msg(debug_proc, LOG_DEBUG, " (Link flags: %s; %s,%s)", (res.diropres_u.diropres.attributes.mode & S_ISVTX) ? "trigger" : "not trigger", (res.diropres_u.diropres.attributes.mode & S_ISUID) ? "mounted" : "not mounted", (res.diropres_u.diropres.attributes.mode & S_ISGID) ? "needs auth." : "no auth. needed"); } sys_msg(debug_proc, LOG_DEBUG, "<- lookup"); gLookupTarget = gNoLookupTarget; return(&res); } readlinkres * nfsproc_readlink_2_svc(nfs_fh *fh, struct svc_req *req) { static readlinkres res; struct file_handle *ifh; Vnode *n; unsigned int status; uid_t uid = -2; /* default = nobody */ ifh = (struct file_handle *)fh; sys_msg(debug_proc, LOG_DEBUG, "-> readlink"); if (req->rq_cred.oa_flavor == AUTH_UNIX) { struct authunix_parms *aup; aup = (struct authunix_parms *)req->rq_clntcred; uid = aup->aup_uid; sys_msg(debug_proc, LOG_DEBUG, " requesting uid = %ld", uid); } else { sys_msg(debug_proc, LOG_DEBUG, " requesting uid = unknown"); } sys_msg(debug_proc, LOG_DEBUG, " fh = %s", fhtoc(fh)); rpc_xid = su_data(req->rq_xprt)->su_xid; sys_msg(debug_proc, LOG_DEBUG, " xid = 0x%08lx", rpc_xid); if (doing_timeout) { sys_msg(debug_proc, LOG_DEBUG, "<- readlink (doing_timeout)"); return NULL; } n = [controller vnodeWithID:ifh->node_id]; if (n == nil) { sys_msg(debug, LOG_WARNING, "readlink for non-existent file handle %s", fhtoc(fh)); res.status = NFSERR_NOENT; } else if ([n type] != NFLNK) res.status = NFSERR_ISDIR; else res.status = [n nfsStatus]; if (res.status != NFS_OK) { sys_msg(debug_proc, LOG_DEBUG, "<- readlink (1)"); return(&res); } status = 0; #warning test here for a bad afp mount ... (i.e. disconnected) if ([n type] == NFLNK) { sys_msg(debug_proc, LOG_DEBUG, " NFLNK"); } if ([controller checkMountInProgressForTransaction:rpc_xid]) { sys_msg(debug_proc, LOG_DEBUG, " mount is in progress"); sys_msg(debug_proc, LOG_DEBUG, "<- [readlink abandoned]"); (void)usleep(100000UL); /* Sleep for 0.1 Sec. (100,000 uSec.) */ return NULL; /* Try again later... */ }; if (([n type] == NFLNK) && (![n mounted])) { sys_msg(debug_proc, LOG_DEBUG, " not mounted"); #warning deriving internal copy of private and transport-specific xid... status = [[n map] mount:n withUid:uid]; [[n server] reset]; if (gBlockedMountDependency) { sys_msg(debug_proc, LOG_DEBUG, " mount is in progress; transaction id = 0x%08lx", gBlockingMountTransactionID); if (gBlockingMountTransactionID == rpc_xid) { /* This is a retransmission of the original readlink request that triggered the mount in the first place; best to wait until the mount's complete. */ sys_msg(debug_proc, LOG_DEBUG, "<- [readlink abandoned]"); gBlockedMountDependency = NO; /* This is a one-shot deal */ return NULL; /* Try again later... */ }; /* This is a mount in progress from another call - don't wait up and don't hold up THIS call! */ gBlockedMountDependency = NO; }; if (gForkedMountInProgress) { sys_msg(debug_proc, LOG_DEBUG, "<- [readlink abandoned]"); gForkedMountInProgress = NO; return NULL; }; if (status != 0) { res.status = (status == ECANCELED) ? ECANCELED : NFSERR_NOENT; sys_msg(debug_proc, LOG_DEBUG, "<- readlink (2)"); return(&res); } } sys_msg(debug_proc, LOG_DEBUG, " name = %s", [[n name] value]); res.readlinkres_u.data = [[n link] value]; [n markAccessTime]; sys_msg(debug_proc, LOG_DEBUG, " link = %s", res.readlinkres_u.data); sys_msg(debug_proc, LOG_DEBUG, "<- readlink"); return(&res); } /* Does something */ readdirres * nfsproc_readdir_2_svc(readdirargs *args, struct svc_req *req) { static readdirres res; Vnode *n, *v; struct entry *e, *nexte; struct entry **entp; unsigned int cookie, count, entrycount, i, nlist; struct file_handle *ifh; Array *list; String *s; ifh = (struct file_handle *)&(args->dir); cookie = *(unsigned int*)args->cookie; sys_msg(debug_proc, LOG_DEBUG, "-> readdir"); sys_msg(debug_proc, LOG_DEBUG, " dir fh = %s", fhtoc(&(args->dir))); sys_msg(debug_proc, LOG_DEBUG, " cookie = %u", cookie); sys_msg(debug_proc, LOG_DEBUG, " count = %u", args->count); res.status = NFS_OK; /* * Free up old stuff */ e = res.readdirres_u.reply.entries; while (e != NULL) { nexte = e->nextentry; free(e); e = nexte; } res.readdirres_u.reply.entries = NULL; n = [controller vnodeWithID:ifh->node_id]; if (n == nil) { sys_msg(debug, LOG_WARNING, "readdir for non-existent file handle %s", fhtoc(&(args->dir))); res.status = NFSERR_NOENT; } else if ([n type] != NFDIR) res.status = NFSERR_NOTDIR; if (res.status != NFS_OK) { sys_msg(debug_proc, LOG_DEBUG, "<- readdir (1)"); return(&res); } sys_msg(debug_proc, LOG_DEBUG, " name = %s", [[n name] value]); gLookupTarget = gNFSReaddir; list = [n dirlist]; nlist = [list count]; count = JUNKSIZE; entrycount = 0; entp = &res.readdirres_u.reply.entries; for (i = cookie; i < nlist; i++) { v = [list objectAtIndex:i]; if (i == 0) s = dot; else if (i == 1) s = dotdot; else s = [v name]; count += ENTRYSIZE; count += BYTES_PER_XDR_UNIT * (([s length] + BYTES_PER_XDR_UNIT - 1) / BYTES_PER_XDR_UNIT); if (count > args->count) { sys_msg(debug_proc, LOG_DEBUG, " BREAK"); break; } sys_msg(debug_proc, LOG_DEBUG, " %4u: %u %s", cookie, [v nodeID], [s value]); *entp = (struct entry *) malloc(sizeof(struct entry)); bzero(*entp, sizeof(struct entry)); (*entp)->fileid = [v nodeID]; (*entp)->name = [s value]; *(unsigned int*)((*entp)->cookie) = ++cookie; (*entp)->nextentry = NULL; entp = &(*entp)->nextentry; } if (i < nlist) res.readdirres_u.reply.eof = FALSE; else res.readdirres_u.reply.eof = TRUE; sys_msg(debug_proc, LOG_DEBUG, " eof = %s", res.readdirres_u.reply.eof ? "TRUE" : "FALSE"); [list release]; sys_msg(debug_proc, LOG_DEBUG, "<- readdir"); return(&res); } statfsres * nfsproc_statfs_2_svc(nfs_fh *fh, struct svc_req *req) { static statfsres res; sys_msg(debug_proc, LOG_DEBUG, "-> statfs"); res.status = NFS_OK; res.statfsres_u.reply.tsize = 512; res.statfsres_u.reply.bsize = 512; res.statfsres_u.reply.blocks = 0; res.statfsres_u.reply.bfree = 0; res.statfsres_u.reply.bavail = 0; sys_msg(debug_proc, LOG_DEBUG, "<- statfs"); return(&res); } /* * These routines do nothing - they should never even be called! */ void * nfsproc_null_2_svc(void *x, struct svc_req *req) { sys_msg(debug_proc, LOG_DEBUG, "-- null"); return((void *)NULL); } attrstat * nfsproc_setattr_2_svc(sattrargs *args, struct svc_req *req) { static attrstat astat; sys_msg(debug_proc, LOG_DEBUG, "-- setattr"); astat.status = NFSERR_ROFS; return(&astat); } void * nfsproc_root_2_svc(void *x, struct svc_req *req) { sys_msg(debug_proc, LOG_DEBUG, "-- root"); return(NULL); } readres * nfsproc_read_2_svc(readargs *args, struct svc_req *req) { static readres res; sys_msg(debug_proc, LOG_DEBUG, "-- read"); res.status = NFSERR_ISDIR; /* XXX: should return better error */ return(&res); } void * nfsproc_writecache_2_svc(void *x, struct svc_req *req) { sys_msg(debug_proc, LOG_DEBUG, "-- writecache"); return(NULL); } attrstat * nfsproc_write_2_svc(writeargs *args, struct svc_req *req) { static attrstat res; sys_msg(debug_proc, LOG_DEBUG, "-- write"); res.status = NFSERR_ROFS; /* XXX: should return better error */ return(&res); } diropres * nfsproc_create_2_svc(createargs *args, struct svc_req *req) { static diropres res; sys_msg(debug_proc, LOG_DEBUG, "-- create"); res.status = NFSERR_ROFS; return(&res); } nfsstat * nfsproc_remove_2_svc(diropargs *args, struct svc_req *req) { static nfsstat status; struct file_handle *ifh; Vnode *n; String *s; uid_t uid = -2; /* default = nobody */ sys_msg(debug_proc, LOG_DEBUG, "-> remove"); if (req->rq_cred.oa_flavor == AUTH_UNIX) { struct authunix_parms *aup; aup = (struct authunix_parms *)req->rq_clntcred; uid = aup->aup_uid; sys_msg(debug_proc, LOG_DEBUG, " requesting uid = %ld", uid); } else { sys_msg(debug_proc, LOG_DEBUG, " requesting uid = unknown"); } sys_msg(debug_proc, LOG_DEBUG, " dir fh = %s", fhtoc(&(args->dir))); sys_msg(debug_proc, LOG_DEBUG, " file = %s", args->name); status = NFS_OK; gLookupTarget = args->name; if (uid != 0) { sys_msg(debug, LOG_WARNING, "remove request from unauthorized uid %ld", uid); status = NFSERR_ACCES; gLookupTarget = gNoLookupTarget; return(&status); } ifh = (struct file_handle *) &(args->dir); n = [controller vnodeWithID:ifh->node_id]; if (n == nil) { sys_msg(debug, LOG_WARNING, "remove for non-existent dir handle %s", fhtoc(&(args->dir))); status = NFSERR_NOENT; } if (status != NFS_OK) { sys_msg(debug_proc, LOG_DEBUG, "<- remove (error %d)", status); gLookupTarget = gNoLookupTarget; return(&status); } s = [String uniqueString:args->name]; status = [n remove:s]; [s release]; if (status != NFS_OK) sys_msg(debug_proc, LOG_DEBUG, "<- remove (res=%d)", status); else sys_msg(debug_proc, LOG_DEBUG, "<- remove"); gLookupTarget = gNoLookupTarget; return(&status); } nfsstat * nfsproc_rename_2_svc(renameargs *args, struct svc_req *req) { static nfsstat status; sys_msg(debug_proc, LOG_DEBUG, "-- rename"); status = NFSERR_ROFS; return(&status); } nfsstat * nfsproc_link_2_svc(linkargs *args, struct svc_req *req) { static nfsstat status; sys_msg(debug_proc, LOG_DEBUG, "-- link"); status = NFSERR_ROFS; return(&status); } nfsstat * nfsproc_symlink_2_svc(symlinkargs *args, struct svc_req *req) { static nfsstat status; struct file_handle *ifh; Vnode *n; uid_t uid = -2; /* default = nobody */ char timestring[FORMATTED_TIME_LEN]; sys_msg(debug_proc, LOG_DEBUG, "-> symlink"); if (req->rq_cred.oa_flavor == AUTH_UNIX) { struct authunix_parms *aup; aup = (struct authunix_parms *)req->rq_clntcred; uid = aup->aup_uid; sys_msg(debug_proc, LOG_DEBUG, " requesting uid = %ld", uid); } else { sys_msg(debug_proc, LOG_DEBUG, " requesting uid = unknown"); } sys_msg(debug_proc, LOG_DEBUG, " from.dir fh = %s", fhtoc(&(args->from.dir))); sys_msg(debug_proc, LOG_DEBUG, " from.name = %s", args->from.name); sys_msg(debug_proc, LOG_DEBUG, " to = %s", args->to); sys_msg(debug_proc, LOG_DEBUG, " attributes:"); sys_msg(debug_proc, LOG_DEBUG, " mode = 0%o", args->attributes.mode); sys_msg(debug_proc, LOG_DEBUG, " uid = 0%ld", args->attributes.uid); sys_msg(debug_proc, LOG_DEBUG, " gid = 0%ld", args->attributes.gid); sys_msg(debug_proc, LOG_DEBUG, " size = 0x%x", args->attributes.size); sys_msg(debug_proc, LOG_DEBUG, " atime = %s", formattimevalue(&args->attributes.atime, timestring, sizeof(timestring))); sys_msg(debug_proc, LOG_DEBUG, " mtime = %s", formattimevalue(&args->attributes.mtime, timestring, sizeof(timestring))); status = NFS_OK; gLookupTarget = args->to; if (uid != 0) { sys_msg(debug, LOG_WARNING, "symlink request from unauthorized uid"); status = NFSERR_ACCES; gLookupTarget = gNoLookupTarget; return(&status); }; ifh = (struct file_handle *)&(args->from.dir); n = [controller vnodeWithID:ifh->node_id]; if (n == nil) { sys_msg(debug, LOG_WARNING, "lookup for non-existent file handle %s", fhtoc(&(args->from.dir))); status = NFSERR_NOENT; } if (status != NFS_OK) { sys_msg(debug_proc, LOG_DEBUG, "<- symlink (error %d)", status); gLookupTarget = gNoLookupTarget; return(&status); } status = [n symlinkWithName:args->from.name to:args->to attributes:(struct nfsv2_sattr *)&args->attributes]; if (status != NFS_OK) { sys_msg(debug_proc, LOG_DEBUG, "<- symlink (status = %d)", status); } else { sys_msg(debug_proc, LOG_DEBUG, "<- symlink"); }; gLookupTarget = gNoLookupTarget; return(&status); } diropres * nfsproc_mkdir_2_svc(createargs *args, struct svc_req *req) { static diropres res; sys_msg(debug_proc, LOG_DEBUG, "-- mkdir"); res.status = NFSERR_ROFS; return(&res); } nfsstat * nfsproc_rmdir_2_svc(diropargs *args, struct svc_req *req) { static nfsstat status; sys_msg(debug_proc, LOG_DEBUG, "-- rmdir"); status = NFSERR_ROFS; return(&status); }