/* * Copyright, 1991, 1992, by Leendert van Doorn (leendert@cs.vu.nl) * * This material is copyrighted by Leendert van Doorn, 1991, 1992. The usual * standard disclaimer applies, especially the fact that the author nor the * Vrije Universiteit, Amsterdam are liable for any damages caused by direct or * indirect use of the information or functionality provided by this program. */ /* * nfs - A shell that provides access to NFS file systems */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mount.h" #include "nfs_prot.h" /* * Fundamental constants */ #define NARGVEC 100 /* maximum number of arguments */ /* * List of command identifiers */ #define CMD_UNKNOWN 0 /* unknown command */ #define CMD_HOST 1 /* host */ #define CMD_UID 2 /* uid [] */ #define CMD_GID 3 /* gid [] */ #define CMD_CD 4 /* cd [] */ #define CMD_LCD 5 /* lcd [] */ #define CMD_CAT 6 /* cat */ #define CMD_LS 7 /* ls [-l] */ #define CMD_GET 8 /* get */ #define CMD_DF 9 /* df */ #define CMD_MOUNT 10 /* mount [-u] [-p] [:] */ #define CMD_UMOUNT 11 /* umount */ #define CMD_UMOUNTALL 12 /* umountall */ #define CMD_EXPORT 13 /* export */ #define CMD_DUMP 14 /* dump */ #define CMD_STATUS 15 /* status */ #define CMD_HELP 16 /* help */ #define CMD_QUIT 17 /* quit */ #define CMD_RM 18 /* rm */ #define CMD_LN 19 /* ln */ #define CMD_MV 20 /* mv */ #define CMD_MKDIR 21 /* mkdir */ #define CMD_RMDIR 22 /* rmdir */ #define CMD_CHMOD 23 /* chmod */ #define CMD_CHOWN 24 /* chown [.] */ #define CMD_PUT 25 /* put [] */ #define CMD_HANDLE 26 /* handle [] */ #define CMD_MKNOD 27 /* mknod */ #define CMD_MROOT 28 /* mroot */ /* * Key word table */ struct keyword { char *kw_command; int kw_value; char *kw_help; } keyword[] = { "host", CMD_HOST, " - set remote host name", "uid", CMD_UID, "[] - set remote user id", "gid", CMD_GID, "[] - set remote group id", "cd", CMD_CD, "[] - change remote working directory", "lcd", CMD_LCD, "[] - change local working directory", "cat", CMD_CAT, " - display remote file", "ls", CMD_LS, "[-l] - list remote directory", "get", CMD_GET, " - get remote files", "df", CMD_DF, "- file system information", "rm", CMD_RM, " - delete remote file", "ln", CMD_LN, " - link file", "mv", CMD_MV, " - move file", "mkdir", CMD_MKDIR, " - make remote directory", "rmdir", CMD_RMDIR, " - remove remote directory", "chmod", CMD_CHMOD, " - change mode", "chown", CMD_CHOWN, "[.] - change owner", "put", CMD_PUT, " [] - put file", "mount", CMD_MOUNT, "[-u] [-p] [:] - mount file system", "umount", CMD_UMOUNT, "- umount remote file system", "umountall",CMD_UMOUNTALL, "- umount all remote file systems", "export", CMD_EXPORT, "- show all exported file systems", "dump", CMD_DUMP, "- show all remote mounted file systems", "status", CMD_STATUS, "- general status report", "help", CMD_HELP, "- this help message", "quit", CMD_QUIT, "- name says it all", "handle", CMD_HANDLE, "[] - get/set directory file handle", "mknod", CMD_MKNOD, "mknod - make devices", "mroot", CMD_MROOT, "- attempt to masquerade as root" }; /* run-time settable flags */ int verbose = 1; /* verbosity flag */ int interact = 1; /* interactive mode */ /* user provided credentials */ int uid = -2; /* remote user id (initialy nobody) */ int gid = -2; /* remote group id (initialy nobody) */ /* server information (also used as state information) */ char *mountpath; /* remote mount path */ char *remotehost; /* remote host name */ struct sockaddr_in server_addr; /* remote server address information */ struct sockaddr_in mntserver_addr; /* remote mount server address */ struct sockaddr_in nfsserver_addr; /* remote mount server address */ CLIENT *mntclient; /* mount RPC client */ CLIENT *nfsclient; /* nfs RPC client */ fhstatus *mountpoint; /* remote mount point */ fhandle directory_handle; /* current directory handle */ struct timeval timeout = { 60, 0 }; /* default time out */ int transfersize; /* NFS default transfer size */ /* interrupt environments */ jmp_buf intenv; /* where to go in interrupts */ fhstatus *pmap_mnt(); char *malloc(); char *calloc(); char *realloc(); char *strsave(); char *strchr(); char *nfs_error(); char *getenv(); void interrupt(); main(argc, argv) int argc; char **argv; { int opt, cmd, argcount; char *argvec[NARGVEC]; char buffer[BUFSIZ]; /* command line option processing */ while ((opt = getopt(argc, argv, "vi")) != EOF) { switch (opt) { case 'v': verbose = 0; break; case 'i': interact = 0; break; default: fprintf(stderr, "Usage: %s [-v]\n", argv[0]); exit(1); } } signal(SIGINT, interrupt); /* interpreter's main command loop */ if (setjmp(intenv)) putchar('\n'); while (getline(buffer, BUFSIZ, &argcount, argvec, NARGVEC)) { if (argcount == 0) continue; if ((cmd = command(argvec[0])) == CMD_QUIT) break; else switch (cmd) { case CMD_HOST: do_host(argcount, argvec); break; case CMD_UID: do_setid(&uid, argcount, argvec); break; case CMD_GID: do_setid(&gid, argcount, argvec); break; case CMD_CD: do_cd(argcount, argvec); break; case CMD_LCD: do_lcd(argcount, argvec); break; case CMD_CAT: do_cat(argcount, argvec); break; case CMD_LS: do_ls(argcount, argvec); break; case CMD_GET: do_get(argcount, argvec); break; case CMD_DF: do_df(argcount, argvec); break; case CMD_RM: do_rm(argcount, argvec); break; case CMD_LN: do_ln(argcount, argvec); break; case CMD_MV: do_mv(argcount, argvec); break; case CMD_MKDIR: do_mkdir(argcount, argvec); break; case CMD_RMDIR: do_rmdir(argcount, argvec); break; case CMD_CHMOD: do_chmod(argcount, argvec); break; case CMD_CHOWN: do_chown(argcount, argvec); break; case CMD_PUT: do_put(argcount, argvec); break; case CMD_HANDLE: do_handle(argcount, argvec); break; case CMD_MKNOD: do_mknod(argcount, argvec); break; case CMD_MROOT: do_mroot(&uid, argcount, argvec); break; case CMD_MOUNT: do_mount(argcount, argvec); break; case CMD_UMOUNT: do_umount(argcount, argvec); break; case CMD_UMOUNTALL: do_umountall(argcount, argvec); break; case CMD_EXPORT: do_export(argcount, argvec); break; case CMD_DUMP: do_dump(argcount, argvec); break; case CMD_STATUS: do_status(argcount, argvec); break; case CMD_HELP: do_help(argcount, argvec); break; case CMD_UNKNOWN: if (buffer[0] == '!') { system(buffer + 1); printf("!\n"); } else fprintf(stderr, "%s: unrecognized command\n", argvec[0]); break; default: fprintf(stderr, "internal error: '%s' not is case\n", argvec[0]); break; } } if (remotehost) close_mount(); exit(0); } void interrupt() { longjmp(intenv, 1); } /* * Read a line from standard input and break * it up into an argument vector. */ int getline(buf, bufsize, argc, argv, argvsize) char *buf, **argv; int bufsize, *argc, argvsize; { register char *p; if (interact) printf("nfs> "); if (fgets(buf, bufsize, stdin) == NULL) return 0; *argc = 0; for (p = buf; *p == ' ' || *p == '\t'; p++) /* skip white spaces */; while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0') { if (*argc > argvsize) break; argv[(*argc)++] = p; for (; *p != ' ' && *p != '\t' && *p != '\n' && *p != '\0'; p++) /* skip word */; if (*p != '\0') *p++ ='\0'; for (; *p == ' ' || *p == '\t'; p++) /* skip white spaces */; } return 1; } /* * Search for command in keyword table */ int command(cmd) char *cmd; { register int i; for (i = 0; i < sizeof(keyword)/sizeof(struct keyword); i++) if (strcmp(keyword[i].kw_command, cmd) == 0) return keyword[i].kw_value; return CMD_UNKNOWN; } /* * Set remote host and initialize RPC channel * to mount daemon. */ do_host(argc, argv) int argc; char **argv; { if (argc != 2) fprintf(stderr, "Usage: host \n"); else open_mount(argv[1]); } /* * Set user or group id (updating RPC authentication info) */ do_setid(var, argc, argv) int *var, argc; char **argv; { *var = argc == 2 ? atoi(argv[1]) : -2; if (nfsclient && nfsclient->cl_auth) { auth_destroy(nfsclient->cl_auth); nfsclient->cl_auth = authunix_create_default(uid, gid); } } /* * Change remote working directory */ do_cd(argc, argv) int argc; char **argv; { register char *p; char *component; diropargs args; diropres *res; fhandle handle; if (mountpath == NULL) { fprintf(stderr, "cd: no remote file system mounted\n"); return; } /* easy case: cd to root */ if (argc == 1) { bcopy(mountpoint->fhstatus_u.fhs_fhandle, directory_handle, NFS_FHSIZE); return; } /* if a directory start with '/', we search from the root */ if (*(p = argv[1]) == '/') { bcopy(mountpoint->fhstatus_u.fhs_fhandle, handle, NFS_FHSIZE); p++; } else bcopy(directory_handle, handle, NFS_FHSIZE); /* * Break path up into directory components and check every * component for its validity. */ for (;;) { if (*p == '\0') break; for (component = p; *p != '/' && *p != '\0'; p++) /* do nothing */; *p++ = '\0'; args.name = component; bcopy(handle, &args.dir, NFS_FHSIZE); if ((res = nfsproc_lookup_2(&args, nfsclient)) == NULL) { clnt_perror(nfsclient, "nfsproc_lookup"); return; } if (res->status != NFS_OK) { fprintf(stderr, "%s: %s\n", component, nfs_error(res->status)); return; } if (res->diropres_u.diropres.attributes.type != NFDIR) { fprintf(stderr, "%s: is not a directory\n", component); return; } bcopy(&res->diropres_u.diropres.file, handle, NFS_FHSIZE); } bcopy(handle, directory_handle, NFS_FHSIZE); } /* * Change local working directory */ do_lcd(argc, argv) int argc; char **argv; { if (argc == 1) { char *home = getenv("HOME"); if (home != NULL) if (chdir(home) != 0) perror("lcd"); } else if (chdir(argv[1]) != 0) perror("lcd"); } /* * Display a remote file */ do_cat(argc, argv) int argc; char **argv; { diropargs dargs; diropres *dres; readargs rargs; readres *rres; long offset; if (mountpath == NULL) { fprintf(stderr, "cat: no remote file system mounted\n"); return; } if (argc != 2) { fprintf(stderr, "Usage: cat \n"); return; } /* lookup name in current directory */ dargs.name = argv[1]; bcopy(directory_handle, &dargs.dir, NFS_FHSIZE); if ((dres = nfsproc_lookup_2(&dargs, nfsclient)) == NULL) { clnt_perror(nfsclient, "nfsproc_lookup"); return; } if (dres->status != NFS_OK) { fprintf(stderr, "%s: %s\n", argv[1], nfs_error(dres->status)); return; } if (dres->diropres_u.diropres.attributes.type != NFREG) { fprintf(stderr, "%s: is not a regular file\n", argv[1]); return; } bcopy(&dres->diropres_u.diropres.file, &rargs.file, NFS_FHSIZE); for (offset = 0; offset < dres->diropres_u.diropres.attributes.size; ) { rargs.offset = offset; rargs.count = rargs.totalcount = transfersize; if ((rres = nfsproc_read_2(&rargs, nfsclient)) == NULL) { clnt_perror(nfsclient, "nfsproc_read_2"); break; } if (rres->status != NFS_OK) { fprintf(stderr, "%s: %s\n", argv[1], nfs_error(rres->status)); break; } fwrite(rres->readres_u.reply.data.data_val, rres->readres_u.reply.data.data_len, 1, stdout); offset += transfersize; } } /* * List remote directory */ do_ls(argc, argv) int argc; char **argv; { char **table, **ptr, **p; int lflag = 0; argv++; argc--; if (mountpath == NULL) { fprintf(stderr, "ls: no remote file system mounted\n"); return; } if (argc >= 1 && strcmp(argv[0], "-l") == 0) { argv++; argc--; lflag = 1; } if (!getdirentries(directory_handle, &table, &ptr, 20)) return; for (p = table; p < ptr; p++) { if (!match(*p, argc, argv)) continue; if (lflag == 1) printfilestatus(*p); else printf("%s\n", *p); free(*p); } free(table); } /* * Print long listing of a files, much in the way ``ls -l'' does */ printfilestatus(name) char *name; { diropargs args; diropres *res; int mode; args.name = name; bcopy(directory_handle, &args.dir, NFS_FHSIZE); if ((res = nfsproc_lookup_2(&args, nfsclient)) == NULL) { clnt_perror(nfsclient, "nfsproc_lookup"); return; } if (res->status != NFS_OK) { fprintf(stderr, "Lookup failed: %s\n", nfs_error(res->status)); return; } switch (res->diropres_u.diropres.attributes.type) { case NFNON: putchar('s'); break; case NFREG: putchar('-'); break; case NFDIR: putchar('d'); break; case NFBLK: putchar('b'); break; case NFCHR: putchar('c'); break; case NFLNK: putchar('l'); break; default: putchar('?'); break; } mode = res->diropres_u.diropres.attributes.mode; if (mode & 0400) putchar('r'); else putchar('-'); if (mode & 0200) putchar('w'); else putchar('-'); if (mode & 0100) if (mode & 04000) putchar('s'); else putchar('x'); else if (mode & 04000) putchar('S'); else putchar('-'); if (mode & 040) putchar('r'); else putchar('-'); if (mode & 020) putchar('w'); else putchar('-'); if (mode & 010) if (mode & 02000) putchar('s'); else putchar('x'); else if (mode & 02000) putchar('S'); else putchar('-'); if (mode & 04) putchar('r'); else putchar('-'); if (mode & 02) putchar('w'); else putchar('-'); if (mode & 01) if (mode & 01000) putchar('t'); else putchar('x'); else if (mode & 01000) putchar('T'); else putchar('-'); printf("%3d%9d%6d%10d ", res->diropres_u.diropres.attributes.nlink, res->diropres_u.diropres.attributes.uid, res->diropres_u.diropres.attributes.gid, res->diropres_u.diropres.attributes.size); writefiledate(res->diropres_u.diropres.attributes.ctime.seconds); printf(" %s", name); if (res->diropres_u.diropres.attributes.type == NFLNK) { readlinkres *rlres; nfs_fh rlargs; bcopy(&res->diropres_u.diropres.file, &rlargs, NFS_FHSIZE); if ((rlres = nfsproc_readlink_2(&rlargs, nfsclient)) == NULL) { clnt_perror(nfsclient, "nfsproc_readlink"); return; } if (res->status != NFS_OK) { fprintf(stderr, "Lookup failed: %s\n", nfs_error(res->status)); return; } printf(" -> %s\n", rlres->readlinkres_u.data); } else putchar('\n'); } int writefiledate(d) time_t d; { time_t now, sixmonthsago, onehourfromnow; char *cp; (void) time(&now); sixmonthsago = now - 6L*30L*24L*60L*60L; onehourfromnow = now + 60L*60L; cp = ctime(&d); if ((d < sixmonthsago) || (d > onehourfromnow)) return printf(" %-7.7s %-4.4s ", cp+4, cp+20); else return printf(" %-12.12s ", cp+4); } /* * Get remote files */ do_get(argc, argv) int argc; char **argv; { char **table, **ptr, **p; char answer[512]; diropargs args; diropres *res; readargs rargs; readres *rres; int iflag = 0; long offset; FILE *fp; argv++; argc--; if (mountpath == NULL) { fprintf(stderr, "get: no remote file system mounted\n"); return; } if (argc >= 1 && strcmp(argv[0], "-i") == 0) { argv++; argc--; iflag = 1; } if (!getdirentries(directory_handle, &table, &ptr, 20)) return; for (p = table; p < ptr; p++) { /* match before going over the wire */ if (!match(*p, argc, argv)) continue; /* only regular files can be transfered */ args.name = *p; bcopy(directory_handle, &args.dir, NFS_FHSIZE); if ((res = nfsproc_lookup_2(&args, nfsclient)) == NULL) { clnt_perror(nfsclient, "nfsproc_lookup"); return; } if (res->status != NFS_OK) { fprintf(stderr, "Lookup failed: %s\n", nfs_error(res->status)); return; } if (res->diropres_u.diropres.attributes.type != NFREG) continue; /* ask for confirmation */ printf("%s? ", *p); if (!iflag) { gets(answer); if (answer[0] != 'y' && answer[0] != 'Y') continue; } else printf("Yes\n"); /* get actual file */ if ((fp = fopen(*p, "w")) == NULL) { fprintf(stderr, "get: cannot create %s\n", *p); continue; } bcopy(&res->diropres_u.diropres.file, &rargs.file, NFS_FHSIZE); for (offset = 0; offset < res->diropres_u.diropres.attributes.size; ) { rargs.offset = offset; rargs.count = rargs.totalcount = transfersize; if ((rres = nfsproc_read_2(&rargs, nfsclient)) == NULL) { clnt_perror(nfsclient, "nfsproc_read"); break; } if (rres->status != NFS_OK) { fprintf(stderr, "%s: %s\n", argv[1], nfs_error(rres->status)); break; } fwrite(rres->readres_u.reply.data.data_val, rres->readres_u.reply.data.data_len, 1, fp); offset += transfersize; } fclose(fp); free(*p); } free(table); } /* * Show file system information */ /* ARGUSED */ do_df(argc, argv) int argc; char **argv; { statfsres *res; if (mountpath == NULL) { fprintf(stderr, "df: no remote file system mounted\n"); return; } if (argc != 1) { fprintf(stderr, "Usage: df\n"); return; } if ((res = nfsproc_statfs_2(directory_handle, nfsclient)) == NULL) { clnt_perror(nfsclient, "nfsproc_statfs"); return; } if (res->status != NFS_OK) { fprintf(stderr, "Df failed: %s\n", nfs_error(res->status)); return; } #define x res->statfsres_u.reply printf("%s:%s %dK, %dK used, %dK free (%dK useable).\n", remotehost, mountpath, x.blocks, (x.blocks-x.bfree), x.bfree, x.bavail); if (x.bsize != 1024) printf("Warning: This filesystem has a blocksize of %d bytes.\n", x.bsize); #undef x } /* * Delete a remote file */ do_rm(argc, argv) int argc; char **argv; { diropargs args; nfsstat *res; if (mountpath == NULL) { fprintf(stderr, "rm: no remote file system mounted\n"); return; } if (argc != 2) { fprintf(stderr, "Usage: rm \n"); return; } args.name = argv[1]; bcopy(directory_handle, &args.dir, NFS_FHSIZE); if ((res = nfsproc_remove_2(&args, nfsclient)) == NULL) { clnt_perror(nfsclient, "nfsproc_remove"); return; } if (*res != NFS_OK) { fprintf(stderr, "Remove failed: %s\n", nfs_error(*res)); return; } } /* * Link a file */ do_ln(argc, argv) int argc; char **argv; { diropargs dargs; linkargs largs; diropres *dres; nfsstat *lres; if (mountpath == NULL) { fprintf(stderr, "ln: no remote file system mounted\n"); return; } if (argc != 3) { fprintf(stderr, "Usage: ln \n"); return; } dargs.name = argv[1]; bcopy(directory_handle, &dargs.dir, NFS_FHSIZE); if ((dres = nfsproc_lookup_2(&dargs, nfsclient)) == NULL) { clnt_perror(nfsclient, "nfsproc_lookup"); return; } if (dres->status != NFS_OK) { fprintf(stderr, "%s: %s\n", argv[1], nfs_error(dres->status)); return; } bcopy(&dres->diropres_u.diropres.file, &largs.from, NFS_FHSIZE); largs.to.name = argv[2]; bcopy(directory_handle, &largs.to.dir, NFS_FHSIZE); if ((lres = nfsproc_link_2(&largs, nfsclient)) == NULL) { clnt_perror(nfsclient, "nfsproc_link"); return; } if (*lres != NFS_OK) { fprintf(stderr, "Link failed: %s\n", nfs_error(*lres)); return; } } /* * Move a file or directory */ do_mv(argc, argv) int argc; char **argv; { renameargs args; nfsstat *res; if (mountpath == NULL) { fprintf(stderr, "mv: no remote file system mounted\n"); return; } if (argc != 3) { fprintf(stderr, "Usage: mv \n"); return; } args.from.name = argv[1]; bcopy(directory_handle, &args.from.dir, NFS_FHSIZE); args.to.name = argv[2]; bcopy(directory_handle, &args.to.dir, NFS_FHSIZE); if ((res = nfsproc_rename_2(&args, nfsclient)) == NULL) { clnt_perror(nfsclient, "nfsproc_rename"); return; } if (*res != NFS_OK) { fprintf(stderr, "Rename failed: %s\n", nfs_error(*res)); return; } } /* * Make remote directory */ do_mkdir(argc, argv) int argc; char **argv; { createargs args; diropres *res; if (mountpath == NULL) { fprintf(stderr, "mkdir: no remote file system mounted\n"); return; } if (argc != 2) { fprintf(stderr, "Usage: mkdir \n"); return; } args.where.name = argv[1]; bcopy(directory_handle, &args.where.dir, NFS_FHSIZE); args.attributes.mode = 040755; args.attributes.uid = uid; args.attributes.gid = gid; args.attributes.size = -1; args.attributes.atime.seconds = -1; args.attributes.atime.useconds = -1; args.attributes.mtime.seconds = -1; args.attributes.mtime.useconds = -1; if ((res = nfsproc_mkdir_2(&args, nfsclient)) == NULL) { clnt_perror(nfsclient, "nfsproc_mkdir"); return; } if (res->status != NFS_OK) { fprintf(stderr, "Make directory failed: %s\n", nfs_error(res->status)); return; } } /* * Remove remote directory */ do_rmdir(argc, argv) int argc; char **argv; { diropargs args; nfsstat *res; if (mountpath == NULL) { fprintf(stderr, "rmdir: no remote file system mounted\n"); return; } if (argc != 2) { fprintf(stderr, "Usage: rmdir \n"); return; } args.name = argv[1]; bcopy(directory_handle, &args.dir, NFS_FHSIZE); if ((res = nfsproc_rmdir_2(&args, nfsclient)) == NULL) { clnt_perror(nfsclient, "nfsproc_rmdir"); return; } if (*res != NFS_OK) { fprintf(stderr, "Remove directory failed: %s\n", nfs_error(*res)); return; } } /* * Change mode of remote file or directory */ do_chmod(argc, argv) int argc; char **argv; { sattrargs aargs; diropargs dargs; attrstat *ares; diropres *dres; int mode; if (mountpath == NULL) { fprintf(stderr, "chmod: no remote file system mounted\n"); return; } if (argc != 3) { fprintf(stderr, "Usage: chmod \n"); return; } if (sscanf(argv[1], "%o", &mode) != 1) { fprintf(stderr, "chmod: invalid mode\n"); return; } dargs.name = argv[2]; bcopy(directory_handle, &dargs.dir, NFS_FHSIZE); if ((dres = nfsproc_lookup_2(&dargs, nfsclient)) == NULL) { clnt_perror(nfsclient, "nfsproc_lookup"); return; } if (dres->status != NFS_OK) { fprintf(stderr, "%s: %s\n", argv[2], nfs_error(dres->status)); return; } bcopy(&dres->diropres_u.diropres.file, &aargs.file, NFS_FHSIZE); aargs.attributes.mode = mode; aargs.attributes.uid = -1; aargs.attributes.gid = -1; aargs.attributes.size = -1; aargs.attributes.atime.seconds = -1; aargs.attributes.atime.useconds = -1; aargs.attributes.mtime.seconds = -1; aargs.attributes.mtime.useconds = -1; if ((ares = nfsproc_setattr_2(&aargs, nfsclient)) == NULL) { clnt_perror(nfsclient, "nfsproc_setattr"); return; } if (ares->status != NFS_OK) { fprintf(stderr, "Set attributes failed: %s\n", nfs_error(ares->status)); return; } } /* * Change owner (and group) of remote file or directory */ do_chown(argc, argv) int argc; char **argv; { sattrargs aargs; diropargs dargs; attrstat *ares; diropres *dres; int own_uid, own_gid; if (mountpath == NULL) { fprintf(stderr, "chown: no remote file system mounted\n"); return; } if (argc != 3) { fprintf(stderr, "Usage: chown [.] \n"); return; } if (sscanf(argv[1], "%d.%d", &own_uid, &own_gid) != 2) { own_gid = -1; if (sscanf(argv[1], "%d", &own_uid) != 1) { fprintf(stderr, "chown: invalid uid[.gid]\n"); return; } } dargs.name = argv[2]; bcopy(directory_handle, &dargs.dir, NFS_FHSIZE); if ((dres = nfsproc_lookup_2(&dargs, nfsclient)) == NULL) { clnt_perror(nfsclient, "nfsproc_lookup"); return; } if (dres->status != NFS_OK) { fprintf(stderr, "%s: %s\n", argv[2], nfs_error(dres->status)); return; } bcopy(&dres->diropres_u.diropres.file, &aargs.file, NFS_FHSIZE); aargs.attributes.mode = -1; aargs.attributes.uid = own_uid; aargs.attributes.gid = own_gid; aargs.attributes.size = -1; aargs.attributes.atime.seconds = -1; aargs.attributes.atime.useconds = -1; aargs.attributes.mtime.seconds = -1; aargs.attributes.mtime.useconds = -1; if ((ares = nfsproc_setattr_2(&aargs, nfsclient)) == NULL) { clnt_perror(nfsclient, "nfsproc_setattr"); return; } if (ares->status != NFS_OK) { fprintf(stderr, "Set attributes failed: %s\n", nfs_error(ares->status)); return; } } /* * Put file from local to remote */ do_put(argc, argv) int argc; char **argv; { createargs cargs; diropargs dargs; diropres *cres; diropres *dres; char buf[BUFSIZ]; fhandle handle; FILE *fp; int n; long offset; if (mountpath == NULL) { fprintf(stderr, "put: no remote file system mounted\n"); return; } if (argc != 2 && argc != 3) { fprintf(stderr, "Usage: put []\n"); return; } if ((fp = fopen(argv[1], "r")) == NULL) { fprintf(stderr, "put: cannot open %s\n", argv[1]); return; } /* * Create remote file name */ cargs.where.name = argc == 3 ? argv[2] : argv[1]; bcopy(directory_handle, &cargs.where.dir, NFS_FHSIZE); cargs.attributes.mode = 0666; cargs.attributes.uid = uid; cargs.attributes.gid = gid; cargs.attributes.size = -1; cargs.attributes.atime.seconds = -1; cargs.attributes.atime.useconds = -1; cargs.attributes.mtime.seconds = -1; cargs.attributes.mtime.useconds = -1; if ((cres = nfsproc_create_2(&cargs, nfsclient)) == NULL) { clnt_perror(nfsclient, "nfsproc_create"); fclose(fp); return; } if (cres->status != NFS_OK) fprintf(stderr, "WARNING: Create failed: %s\n", nfs_error(*cres)); /* * Look up remote file name, to get its handle */ dargs.name = argc == 3 ? argv[2] : argv[1]; bcopy(directory_handle, &dargs.dir, NFS_FHSIZE); if ((dres = nfsproc_lookup_2(&dargs, nfsclient)) == NULL) { clnt_perror(nfsclient, "nfsproc_lookup"); fclose(fp); return; } if (dres->status != NFS_OK) { fprintf(stderr, "%s: %s\n", argv[1], nfs_error(dres->status)); fclose(fp); return; } bcopy(&dres->diropres_u.diropres.file, handle, NFS_FHSIZE); for (offset = 0; (n = fread(buf, 1, sizeof(buf), fp)) > 0; offset += n) { writeargs wargs; attrstat *wres; bcopy(handle, &wargs.file, NFS_FHSIZE); wargs.beginoffset = wargs.offset = offset; wargs.totalcount = n; wargs.data.data_len = n; wargs.data.data_val = buf; if ((wres = nfsproc_write_2(&wargs, nfsclient)) == NULL) { clnt_perror(nfsclient, "nfsproc_write"); fclose(fp); return; } if (wres->status != NFS_OK) { fprintf(stderr, "Write failed: %s\n", nfs_error(wres->status)); fclose(fp); return; } } fclose(fp); } /* * Get/set file handle */ do_handle(argc, argv) int argc; char **argv; { register char *p; register int i; int sock; if (argc <= 1) { if (mountpath == NULL) { fprintf(stderr, "handle: no remote file system mounted\n"); return; } printf("%s:", mountpath); for (i = 0, p = (char *)directory_handle; i < NFS_FHSIZE; i++) printf(" %02x", *p++ & 0xFF); printf("\n"); return; } if (argc != NFS_FHSIZE + 1) { fprintf(stderr, "Usage: handle []\n"); return; } /* umount previous mounted remote file system */ if (mountpath != NULL) close_nfs(); /* setup communication channel with NFS server */ sock = privileged(); nfsserver_addr = server_addr; if ((nfsclient = clntudp_create(&nfsserver_addr, NFS_PROGRAM, NFS_VERSION, timeout, &sock)) == NULL) { clnt_pcreateerror("nfs clntudp_create"); return 0; } clnt_control(nfsclient, CLSET_TIMEOUT, &timeout); nfsclient->cl_auth = authunix_create_default(uid, gid); /* provide some generic name for it */ if ((mountpath = malloc(8 + 1)) == NULL) { fprintf(stderr, "internal error: no more core for mountpath\n"); return; } strcpy(mountpath, ""); /* copy handle from command line argument */ for (i = 0, p = (char *)directory_handle; i < NFS_FHSIZE; i++) *p++ = (char) xtoa(argv[i + 1]); /* get transfer size */ if ((transfersize = determine_transfersize()) <= 0) { fprintf(stderr, "Handle failed: Unable to obtain transfer size\n"); return; } } /* * Create a character device */ do_mknod(argc, argv) int argc; char **argv; { createargs cargs; diropargs dargs; diropres *cres; diropres *dres; char buf[BUFSIZ]; fhandle handle; FILE *fp; int n; long offset; if (mountpath == NULL) { fprintf(stderr, "mknod: no remote file system mounted\n"); return; } if (argc != 4) { fprintf(stderr, "Usage: mknod ]\n"); return; } printf("%s\n", argv[1]); cargs.where.name=argv[1]; bcopy(directory_handle, &cargs.where.dir, NFS_FHSIZE); cargs.attributes.mode = IFCHR; cargs.attributes.uid = uid; cargs.attributes.gid = gid; cargs.attributes.size = makedev(0, 0); cargs.attributes.atime.seconds = -1; cargs.attributes.atime.useconds = -1; cargs.attributes.mtime.seconds = -1; cargs.attributes.mtime.useconds = -1; if ((cres = nfsproc_create_2(&cargs, nfsclient)) == NULL) { clnt_perror(nfsclient, "nfsproc_create"); return; } } /* * Attempt to masquerade as root (and update RPC authenticaton info) */ do_mroot(var, argc, argv) int *var, argc; char **argv; { *var = 0x00100000; if (nfsclient && nfsclient->cl_auth) { auth_destroy(nfsclient->cl_auth); nfsclient->cl_auth = authunix_create_default(uid, gid); } /* * Put in some type of code here * to find a directory owned by root * and write a zero byte file to it. * once written, it would be possible * to test to make sure this method * works and tell the person upon * startup. */ } int xtoa(s) char *s; { register int n; for (n = 0; *s; s++) { n <<= 4; if (*s >= '0' && *s <= '9') n += *s - '0'; else if (*s >= 'a' && *s <= 'f') n += *s - 'a' + 10; else if (*s >= 'A' && *s <= 'F') n += *s - 'A' + 10; else break; } return n; } /* * Set up a channel to the NFS server and * mount remote file system. */ do_mount(argc, argv) int argc; char **argv; { char *host, *path; int umount = 0, portmap = 0; argv++; argc--; if (argc >= 1 && strcmp(argv[0], "-u") == 0) { argv++; argc--; umount = 1; } if (argc >= 1 && strcmp(argv[0], "-p") == 0) { argv++; argc--; portmap = 1; } if (argc == 0) { fprintf(stderr, "Usage: mount [-u] [-p] [:]\n"); return; } if ((path = strchr(host = argv[0], ':')) != NULL) { *path++ = '\0'; /* this heuristic is far too simple */ if (remotehost && strcmp(remotehost, host) == 0) { if (mountpath) close_nfs(); } else if (!open_mount(host)) return; } else path = argv[0]; if (remotehost == NULL) { fprintf(stderr, "mount: no host specified\n"); return; } open_nfs(path, umount, portmap); } /* * Unmount remote file system, and close * RPC channel. */ /* ARGUSED */ do_umount(argc, argv) int argc; char **argv; { if (argc != 1) { fprintf(stderr, "Usage: umount\n"); return; } if (mountpath == NULL) fprintf(stderr, "umount: no remote file system mounted\n"); else close_nfs(); } /* * Unmount all remote file system from this host */ /* ARGUSED */ do_umountall(argc, argv) int argc; char **argv; { if (argc != 1) { fprintf(stderr, "Usage: umountall\n"); return; } if (remotehost == NULL) { fprintf(stderr, "umountall: no host specified\n"); return; } if (mountpath != NULL) close_nfs(); (void) mountproc_umntall_1(NULL, mntclient); } /* * Display all exported file systems on remote system */ /* ARGUSED */ do_export(argc, argv) int argc; char **argv; { exports ex, *exp; groups gr; if (argc != 1) { fprintf(stderr, "Usage: export\n"); return; } if (remotehost == NULL) { fprintf(stderr, "export: no host specified\n"); return; } if ((exp = mountproc_export_1(NULL, mntclient)) == NULL) { clnt_perror(mntclient, "mountproc_export"); return; } printf("Export list for %s:\n", remotehost); for (ex = *exp; ex != NULL; ex = ex->ex_next) { printf("%-25s", ex->ex_dir); if (strlen(ex->ex_dir) >= 25) printf("\n "); if ((gr = ex->ex_groups) == NULL) printf("everyone"); while (gr) { printf("%s ", gr->gr_name); gr = gr->gr_next; } putchar('\n'); } } /* * Display all remote mounted file systems */ /* ARGUSED */ do_dump(argc, argv) int argc; char **argv; { mountlist ml, *mlp; if (argc != 1) { fprintf(stderr, "Usage: dump\n"); return; } if (remotehost == NULL) { fprintf(stderr, "dump: no host specified\n"); return; } if ((mlp = mountproc_dump_1(NULL, mntclient)) == NULL) { clnt_perror(mntclient, "mountproc_dump"); return; } for (ml = *mlp; ml != NULL; ml = ml->ml_next) printf("%s:%s\n", ml->ml_hostname, ml->ml_directory); } /* * Generic status report */ /* ARGUSED */ do_status(argc, argv) int argc; char **argv; { if (argc != 1) { fprintf(stderr, "Usage: status\n"); return; } printf("User id : %d\n", uid); printf("Group id : %d\n", gid); if (remotehost) printf("Remote host : `%s'\n", remotehost); if (mountpath) printf("Mount path : `%s'\n", mountpath); printf("Transfer size: %d\n", transfersize); } /* * Simple on-line help facility */ /* ARGUSED */ do_help(argc, argv) int argc; char **argv; { register int i; for (i = 0; i < sizeof(keyword)/sizeof(struct keyword); i++) { if (argc == 2 && strcmp(keyword[i].kw_command, argv[1]) != 0) continue; printf("%s %s\n", keyword[i].kw_command, keyword[i].kw_help); } } /* * Open a channel to remote Mount daemon, * possibly closing an already open connection. */ int open_mount(host) char *host; { int sock = privileged(); /* close previous mounted host */ if (remotehost != NULL) close_mount(); /* convert hostname to IP address */ if (isdigit(*host)) { server_addr.sin_addr.s_addr = inet_addr(host); } else { struct hostent *hp = gethostbyname(host); if (hp == NULL) { fprintf(stderr, "%s: unknown host\n", host); return 0; } bcopy(hp->h_addr, &server_addr.sin_addr.s_addr, hp->h_length); host = hp->h_name; } server_addr.sin_family = AF_INET; server_addr.sin_port = 0; /* set host name */ if ((remotehost = malloc(strlen(host) + 1)) == NULL) { fprintf(stderr, "internal error: no more core for host\n"); return 0; } strcpy(remotehost, host); /* setup communication channel with mount daemon */ mntserver_addr = server_addr; if ((mntclient = clntudp_create(&mntserver_addr, MOUNTPROG, MOUNTVERS, timeout, &sock)) == NULL) { clnt_pcreateerror("mount clntudp_create"); return 0; } clnt_control(mntclient, CLSET_TIMEOUT, &timeout); mntclient->cl_auth = authunix_create_default(0, 0); if (verbose) printf("Open %s (%s)\n", remotehost, inet_ntoa(server_addr.sin_addr)); return 1; } /* * Close channel to mount daemon, * possibly umounting a NFS file system. */ close_mount() { if (mountpath) close_nfs(); if (verbose) printf("Close `%s'\n", remotehost); free(remotehost); remotehost = NULL; if (mntclient) { auth_destroy(mntclient->cl_auth); clnt_destroy(mntclient); } } /* * Mount an NFS file system, perhaps closing * a previous mounted one. The umount option * fools the accounting system. */ int open_nfs(path, umount, portmap) char *path; int umount, portmap; { int sock = privileged(); /* umount previous mounted remote file system */ if (mountpath != NULL) close_nfs(); /* setup communication channel with NFS server */ nfsserver_addr = server_addr; if ((nfsclient = clntudp_create(&nfsserver_addr, NFS_PROGRAM, NFS_VERSION, timeout, &sock)) == NULL) { clnt_pcreateerror("nfs clntudp_create"); return 0; } clnt_control(nfsclient, CLSET_TIMEOUT, &timeout); nfsclient->cl_auth = authunix_create_default(uid, gid); /* * Get file handle for this path from the mount daemon. There * are two ways to get it, either ask it directly or get it * through the port mapper. */ if (portmap) { if ((mountpoint = pmap_mnt(&path, &mntserver_addr)) == NULL) return 0; } else if ((mountpoint = mountproc_mnt_1(&path, mntclient)) == NULL) { clnt_perror(mntclient, "mountproc_mnt"); return 0; } if (mountpoint->fhs_status != NFS_OK) { fprintf(stderr, "Mount failed: %s\n", nfs_error(mountpoint->fhs_status)); return 0; } bcopy(mountpoint->fhstatus_u.fhs_fhandle, directory_handle, NFS_FHSIZE); if (umount) (void) mountproc_umnt_1(&path, mntclient); /* get transfer size */ if ((transfersize = determine_transfersize()) <= 0) { fprintf(stderr, "Mount failed: Unable to obtain transfer size\n"); (void) mountproc_umnt_1(&path, mntclient); return 0; } /* set mount path */ if ((mountpath = malloc(strlen(path) + 1)) == NULL) { fprintf(stderr, "internal error: no more core for mountpath\n"); return 0; } strcpy(mountpath, path); if (verbose) printf("Mount `%s'%s, transfer size %d bytes.\n", mountpath, umount ? " (unmount)":"", transfersize); return 1; } /* * Make a mount call via the port mapper */ fhstatus * pmap_mnt(argp, server_addr) dirpath *argp; struct sockaddr_in *server_addr; { enum clnt_stat stat; static fhstatus res; u_long port; bzero((char *)&res, sizeof(res)); if ((stat = pmap_rmtcall(server_addr, MOUNTPROG, MOUNTVERS, MOUNTPROC_MNT, xdr_dirpath, argp, xdr_fhstatus, &res, timeout, &port)) != RPC_SUCCESS) { clnt_perrno(stat); return NULL; } return &res; } /* * Determine NFS server's transfer size */ static int determine_transfersize() { statfsres *res; if ((res = nfsproc_statfs_2(directory_handle, nfsclient)) == NULL) return 0; if (res->status != NFS_OK) return 0; return res->statfsres_u.reply.tsize; } /* * Acquire a privileged port when possible */ int privileged() { int lport = IPPORT_RESERVED - 1; struct sockaddr_in sin; int s, dontblock = 1; sin.sin_family = AF_INET; sin.sin_addr.s_addr = 0; s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (s < 0) return RPC_ANYSOCK; for (;;) { sin.sin_port = htons((u_short)lport); if (bind(s, (caddr_t)&sin, sizeof (sin), 0) >= 0) { (void)ioctl(s, FIONBIO, (char *) &dontblock); if (verbose) fprintf(stderr, "Using a privileged port (%d)\n", lport); fflush(stderr); return s; } if (errno != EADDRINUSE && errno != EADDRNOTAVAIL) return RPC_ANYSOCK; (lport)--; if (lport == IPPORT_RESERVED/2) { fprintf(stderr, "privileged socket: All ports in use\n"); return RPC_ANYSOCK; } } } /* * Close an NFS mounted file system */ close_nfs() { if (mountpath == NULL) return; if (verbose) printf("Unmount `%s'\n", mountpath); (void) mountproc_umnt_1(&mountpath, mntclient); free(mountpath); mountpath = NULL; if (nfsclient) { auth_destroy(nfsclient->cl_auth); clnt_destroy(nfsclient); } } /* * Returns an auth handle with parameters determined by doing lots of * syscalls. */ AUTH * authunix_create_default(uid, gid) int uid, gid; { char machname[MAX_MACHINE_NAME + 1]; int gids[1]; if (gethostname(machname, MAX_MACHINE_NAME) == -1) { fprintf(stderr, "authunix_create_default: cannot get hostname\n"); exit(1); } machname[MAX_MACHINE_NAME] = 0; gids[0] = gid; return (authunix_create(machname, uid, gid, 1, gids)); } /* * Read all entries (names) in directory 'dirhandle' into * a dynamically build table. It is up to the caller to free * this table. */ getdirentries(dirhandle, table, ptr, nentries) fhandle *dirhandle; register char ***table; register char ***ptr; int nentries; { readdirargs args; readdirres *res; entry *ep; int dircmp(); char **last; *ptr = *table = (char **) calloc(nentries, sizeof(char *)); last = *ptr + nentries; if (*ptr == NULL) { fprintf(stderr, "getdirentries: out of memory\n"); return 0; } bcopy(*dirhandle, &args.dir, NFS_FHSIZE); bzero(args.cookie, NFS_COOKIESIZE); args.count = 1000; for (;;) { if ((res = nfsproc_readdir_2(&args, nfsclient)) == NULL) { clnt_perror(nfsclient, "nfsproc_readdir"); break; } if (res->status != NFS_OK) { fprintf(stderr, "Readdir failed: %s\n", nfs_error(res->status)); break; } ep = res->readdirres_u.reply.entries; while (ep != NULL) { if (*ptr == last) { *table = (char **)realloc(*table, 2*nentries*sizeof(char *)); if (*table == NULL) { fprintf(stderr, "getdirentries: out of memory\n"); exit(1); } *ptr = *table + nentries; last = *ptr + nentries; nentries *= 2; } if ((*(*ptr)++ = strsave(ep->name)) == NULL) return 0; if (ep->nextentry == NULL) break; ep = ep->nextentry; } if (res->readdirres_u.reply.eof) break; bcopy(ep->cookie, args.cookie, NFS_COOKIESIZE); } qsort(*table, *ptr - *table, sizeof(char **), dircmp); return 1; } char * strsave(str) char *str; { char *cp = malloc(strlen(str) + 1); if (cp == NULL) { fprintf(stderr, "strsave: out of memory\n"); return NULL; } strcpy(cp, str); return cp; } int dircmp(p, q) char **p, **q; { return strcmp(*p, *q); } /* * Match string against a normal shell pattern (*?[]) */ int match(s, argc, argv) char *s; int argc; char **argv; { register int i; if (argc == 0) return 1; for (i = 0; i < argc; i++) if (matchpattern(s, argv[i])) return 1; return 0; } int matchpattern(s, p) char *s, *p; { if (*s == '.' && *p != '.') return 0; return amatchpattern(s, p); } int amatchpattern(s, p) register char *s, *p; { register int scc; int c, cc, ok, lc; if (scc = *s++) if ((scc &= 0177) == 0) scc = 0200; switch (c = *p++) { case '[': ok = 0; lc = 077777; while (cc = *p++) { if (cc == ']') { if (ok) return amatchpattern(s, p); else return 0; } else if (cc == '-') { if (lc <= scc && scc <= (c = *p++)) ok++; } else if (scc == (lc = cc)) ok++; } return 0; case '*': return umatchpattern(--s, p); case '\0': return !scc; default: if (c != scc) return 0; case '?': if (scc) return amatchpattern(s, p); return 0; } } int umatchpattern(s, p) register char *s, *p; { if (*p == '\0') return 1; while (*s != '\0') if (amatchpattern(s++, p)) return 1; return 0; } /* * NFS errors */ char * nfs_error(nfsstat) enum nfsstat nfsstat; { switch (nfsstat) { case NFS_OK: return "No error"; case NFSERR_PERM: return "Not owner"; case NFSERR_NOENT: return "No such file or directory"; case NFSERR_IO: return "I/O error"; case NFSERR_NXIO: return "No such device or address"; case NFSERR_ACCES: return "Permission denied"; case NFSERR_EXIST: return "File exists"; case NFSERR_NODEV: return "No such device"; case NFSERR_NOTDIR: return "Not a directory"; case NFSERR_ISDIR: return "Is a directory"; case NFSERR_FBIG: return "File too large"; case NFSERR_NOSPC: return "No space left on device"; case NFSERR_ROFS: return "Read-only file system"; case NFSERR_NAMETOOLONG: return "File name too long"; case NFSERR_NOTEMPTY: return "Directory not empty"; case NFSERR_DQUOT: return "Disc quota exceeded"; case NFSERR_STALE: return "Stale NFS file handle"; case NFSERR_WFLUSH: return "Write cache flushed"; default: return "UKNOWN NFS ERROR"; } }