jaltman-afsd-loopback-w2k-and-noipc-patches-20031121
[openafs.git] / src / WINNT / afsd / fs.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afs/param.h>
11 #include <afs/stds.h>
12
13 #include <windows.h>
14 #include <stdlib.h>
15 #include <malloc.h>
16 #include <string.h>
17 #include <stdio.h>
18 #include <time.h>
19 #include <winsock2.h>
20 #include <errno.h>
21 #include <rx/rx_globals.h>
22
23 #include <osi.h>
24 #include <afsint.h>
25
26 typedef long afs_int32;
27 typedef unsigned long afs_uint32;
28 typedef short afs_int16;
29 typedef unsigned short afs_uint16;
30
31 #include "fs.h"
32 #include "fs_utils.h"
33 #include "cmd.h"
34 #include "afsd.h"
35 #include "cm_ioctl.h"
36
37 #define MAXHOSTS 13
38 #define OMAXHOSTS 8
39 #define MAXNAME 100
40 #define MAXSIZE 2048
41 #define MAXINSIZE 1300    /* pioctl complains if data is larger than this */
42 #define VMSGSIZE 128      /* size of msg buf in volume hdr */
43
44 static char space[MAXSIZE];
45 static char tspace[1024];
46
47 #ifndef WIN32
48 static struct ubik_client *uclient;
49 #endif /* not WIN32 */
50
51 static MemDumpCmd(struct cmd_syndesc *asp);
52
53 extern afs_int32 VL_GetEntryByNameO();
54
55 static char pn[] = "fs";
56 static int rxInitDone = 0;
57
58 static GetCellName();
59
60 #define MAXCELLCHARS            64
61 #define MAXHOSTCHARS            64
62 #define MAXHOSTSPERCELL         8
63
64 struct afsconf_cell {
65         char name[MAXCELLCHARS];
66         short numServers;
67         short flags;
68         struct sockaddr_in hostAddr[MAXHOSTSPERCELL];
69         char hostName[MAXHOSTSPERCELL][MAXHOSTCHARS];
70         char *linkedCell;
71 };
72
73
74 /*
75  * Character to use between name and rights in printed representation for
76  * DFS ACL's.
77  */
78 #define DFS_SEPARATOR   ' '
79
80 typedef char sec_rgy_name_t[1025];      /* A DCE definition */
81
82 struct Acl {
83     int dfs;    /* Originally true if a dfs acl; now also the type
84                    of the acl (1, 2, or 3, corresponding to object,
85                    initial dir, or initial object). */
86     sec_rgy_name_t cell; /* DFS cell name */
87     int nplus;
88     int nminus;
89     struct AclEntry *pluslist;
90     struct AclEntry *minuslist;
91 };
92
93 struct AclEntry {
94     struct AclEntry *next;
95     char name[MAXNAME];
96     afs_int32 rights;
97 };
98
99 void ZapAcl (acl)
100     struct Acl *acl; {
101     ZapList(acl->pluslist);
102     ZapList(acl->minuslist);
103     free(acl);
104 }
105
106 foldcmp (a, b)
107     register char *a;
108     register char *b; {
109     register char t, u;
110     while (1) {
111         t = *a++;
112         u = *b++;
113         if (t >= 'A' && t <= 'Z') t += 0x20;
114         if (u >= 'A' && u <= 'Z') u += 0x20;
115         if (t != u) return 1;
116         if (t == 0) return 0;
117     }
118 }
119
120 /*
121  * Mods for the AFS/DFS protocol translator.
122  *
123  * DFS rights. It's ugly to put these definitions here, but they 
124  * *cannot* change, because they're part of the wire protocol.
125  * In any event, the protocol translator will guarantee these
126  * assignments for AFS cache managers.
127  */
128 #define DFS_READ          0x01
129 #define DFS_WRITE         0x02
130 #define DFS_EXECUTE       0x04
131 #define DFS_CONTROL       0x08
132 #define DFS_INSERT        0x10
133 #define DFS_DELETE        0x20
134
135 /* the application definable ones (backwards from AFS) */
136 #define DFS_USR0 0x80000000      /* "A" bit */
137 #define DFS_USR1 0x40000000      /* "B" bit */
138 #define DFS_USR2 0x20000000      /* "C" bit */
139 #define DFS_USR3 0x10000000      /* "D" bit */
140 #define DFS_USR4 0x08000000      /* "E" bit */
141 #define DFS_USR5 0x04000000      /* "F" bit */
142 #define DFS_USR6 0x02000000      /* "G" bit */
143 #define DFS_USR7 0x01000000      /* "H" bit */
144 #define DFS_USRALL      (DFS_USR0 | DFS_USR1 | DFS_USR2 | DFS_USR3 |\
145                          DFS_USR4 | DFS_USR5 | DFS_USR6 | DFS_USR7)
146
147 /*
148  * Offset of -id switch in command structure for various commands.
149  * The -if switch is the next switch always.
150  */
151 int parm_setacl_id, parm_copyacl_id, parm_listacl_id;
152
153 /*
154  * Determine whether either the -id or -if switches are present, and
155  * return 0, 1 or 2, as appropriate. Abort if both switches are present.
156  */
157 int getidf(as, id)
158     struct cmd_syndesc *as;
159     int id;     /* Offset of -id switch; -if is next switch */
160 {
161     int idf = 0;
162
163     if (as->parms[id].items) {
164         idf |= 1;
165     }
166     if (as->parms[id + 1].items) {
167         idf |= 2;
168     }
169     if (idf == 3) {
170         fprintf
171             (stderr,
172              "%s: you may specify either -id or -if, but not both switches\n",
173              pn);
174         exit(1);
175     }
176     return idf;
177 }
178
179 void PRights (arights, dfs)
180     register afs_int32 arights;
181     int dfs;
182 {
183     if (!dfs) {
184         if (arights & PRSFS_READ) printf("r");
185         if (arights & PRSFS_LOOKUP) printf("l");
186         if (arights & PRSFS_INSERT) printf("i");
187         if (arights & PRSFS_DELETE) printf("d");
188         if (arights & PRSFS_WRITE) printf("w");
189         if (arights & PRSFS_LOCK) printf("k");
190         if (arights & PRSFS_ADMINISTER) printf("a");
191         if (arights & PRSFS_USR0) printf("A");
192         if (arights & PRSFS_USR1) printf("B");
193         if (arights & PRSFS_USR2) printf("C");
194         if (arights & PRSFS_USR3) printf("D");
195         if (arights & PRSFS_USR4) printf("E");
196         if (arights & PRSFS_USR5) printf("F");
197         if (arights & PRSFS_USR6) printf("G");
198         if (arights & PRSFS_USR7) printf("H");
199     } else {
200         if (arights & DFS_READ) printf("r"); else printf("-");
201         if (arights & DFS_WRITE) printf("w"); else printf("-");
202         if (arights & DFS_EXECUTE) printf("x"); else printf("-");
203         if (arights & DFS_CONTROL) printf("c"); else printf("-");
204         if (arights & DFS_INSERT) printf("i"); else printf("-");
205         if (arights & DFS_DELETE) printf("d"); else printf("-");
206         if (arights & (DFS_USRALL)) printf("+");
207         if (arights & DFS_USR0) printf("A");
208         if (arights & DFS_USR1) printf("B");
209         if (arights & DFS_USR2) printf("C");
210         if (arights & DFS_USR3) printf("D");
211         if (arights & DFS_USR4) printf("E");
212         if (arights & DFS_USR5) printf("F");
213         if (arights & DFS_USR6) printf("G");
214         if (arights & DFS_USR7) printf("H");
215     }   
216 }
217
218 /* this function returns TRUE (1) if the file is in AFS, otherwise false (0) */
219 static int InAFS(apath)
220 register char *apath; {
221     struct ViceIoctl blob;
222     register afs_int32 code;
223
224     blob.in_size = 0;
225     blob.out_size = MAXSIZE;
226     blob.out = space;
227
228     code = pioctl(apath, VIOC_FILE_CELL_NAME, &blob, 1);
229     if (code) {
230         if ((errno == EINVAL) || (errno == ENOENT)) return 0;
231     }
232     return 1;
233 }
234
235 /* return a static pointer to a buffer */
236 static char *Parent(apath)
237 char *apath; {
238     register char *tp;
239     strcpy(tspace, apath);
240     tp = strrchr(tspace, '\\');
241     if (tp) {
242         *(tp+1) = 0;    /* lv trailing slash so Parent("k:\foo") is "k:\" not "k:" */
243     }
244     else {
245         fs_ExtractDriveLetter(apath, tspace);
246         strcat(tspace, ".");
247     }
248     return tspace;
249 }
250
251 enum rtype {add, destroy, deny};
252
253 afs_int32 Convert(arights, dfs, rtypep)
254     register char *arights;
255     int dfs;
256     enum rtype *rtypep;
257 {
258     register int i, len;
259     afs_int32 mode;
260     register char tc;
261
262     *rtypep = add;      /* add rights, by default */
263
264     if (dfs) {
265         if (!strcmp(arights, "null")) {
266             *rtypep = deny;
267             return 0;
268         }
269         if (!strcmp(arights,"read")) return DFS_READ | DFS_EXECUTE;
270         if (!strcmp(arights, "write")) return DFS_READ | DFS_EXECUTE | DFS_INSERT | DFS_DELETE | DFS_WRITE;
271         if (!strcmp(arights, "all")) return DFS_READ | DFS_EXECUTE | DFS_INSERT | DFS_DELETE | DFS_WRITE | DFS_CONTROL;
272     } else {
273         if (!strcmp(arights,"read")) return PRSFS_READ | PRSFS_LOOKUP;
274         if (!strcmp(arights, "write")) return PRSFS_READ | PRSFS_LOOKUP | PRSFS_INSERT | PRSFS_DELETE | PRSFS_WRITE | PRSFS_LOCK;
275         if (!strcmp(arights, "mail")) return PRSFS_INSERT | PRSFS_LOCK | PRSFS_LOOKUP;
276         if (!strcmp(arights, "all")) return PRSFS_READ | PRSFS_LOOKUP | PRSFS_INSERT | PRSFS_DELETE | PRSFS_WRITE | PRSFS_LOCK | PRSFS_ADMINISTER;
277     }
278     if (!strcmp(arights, "none")) {
279         *rtypep = destroy; /* Remove entire entry */
280         return 0;
281     }
282     len = strlen(arights);
283     mode = 0;
284     for(i=0;i<len;i++) {
285         tc = *arights++;
286         if (dfs) {
287             if (tc == '-') continue;
288             else if (tc == 'r') mode |= DFS_READ;
289             else if (tc == 'w') mode |= DFS_WRITE;
290             else if (tc == 'x') mode |= DFS_EXECUTE;
291             else if (tc == 'c') mode |= DFS_CONTROL;
292             else if (tc == 'i') mode |= DFS_INSERT;
293             else if (tc == 'd') mode |= DFS_DELETE;
294             else if (tc == 'A') mode |= DFS_USR0;
295             else if (tc == 'B') mode |= DFS_USR1;
296             else if (tc == 'C') mode |= DFS_USR2;
297             else if (tc == 'D') mode |= DFS_USR3;
298             else if (tc == 'E') mode |= DFS_USR4;
299             else if (tc == 'F') mode |= DFS_USR5;
300             else if (tc == 'G') mode |= DFS_USR6;
301             else if (tc == 'H') mode |= DFS_USR7;
302             else {
303                 fprintf(stderr, "%s: illegal DFS rights character '%c'.\n", pn, tc);
304                 exit(1);
305             }
306         } else {
307             if (tc == 'r') mode |= PRSFS_READ;
308             else if (tc == 'l') mode |= PRSFS_LOOKUP;
309             else if (tc == 'i') mode |= PRSFS_INSERT;
310             else if (tc == 'd') mode |= PRSFS_DELETE;
311             else if (tc == 'w') mode |= PRSFS_WRITE;
312             else if (tc == 'k') mode |= PRSFS_LOCK;
313             else if (tc == 'a') mode |= PRSFS_ADMINISTER;
314             else if (tc == 'A') mode |= PRSFS_USR0;
315             else if (tc == 'B') mode |= PRSFS_USR1;
316             else if (tc == 'C') mode |= PRSFS_USR2;
317             else if (tc == 'D') mode |= PRSFS_USR3;
318             else if (tc == 'E') mode |= PRSFS_USR4;
319             else if (tc == 'F') mode |= PRSFS_USR5;
320             else if (tc == 'G') mode |= PRSFS_USR6;
321             else if (tc == 'H') mode |= PRSFS_USR7;
322             else {
323                 fprintf(stderr, "%s: illegal rights character '%c'.\n", pn, tc);
324                 exit(1);
325             }
326         }
327     }
328     return mode;
329 }
330
331 struct AclEntry *FindList (alist, aname)
332     char *aname;
333     register struct AclEntry *alist; {
334     while (alist) {
335         if (!foldcmp(alist->name, aname)) return alist;
336         alist = alist->next;
337     }
338     return 0;
339 }
340
341 /* if no parm specified in a particular slot, set parm to be "." instead */
342 static void SetDotDefault(aitemp)
343 struct cmd_item **aitemp; {
344     register struct cmd_item *ti;
345     if (*aitemp) return;        /* already has value */
346     /* otherwise, allocate an item representing "." */
347     ti = (struct cmd_item *) malloc(sizeof(struct cmd_item));
348     ti->next = (struct cmd_item *) 0;
349     ti->data = (char *) malloc(2);
350     strcpy(ti->data, ".");
351     *aitemp = ti;
352 }
353
354 void ChangeList (al, plus, aname, arights)
355     struct Acl *al;
356     char *aname;
357     afs_int32 arights, plus; {
358     struct AclEntry *tlist;
359     tlist = (plus ? al->pluslist : al->minuslist);
360     tlist = FindList (tlist, aname);
361     if (tlist) {
362         /* Found the item already in the list. */
363         tlist->rights = arights;
364         if (plus)
365             al->nplus -= PruneList(&al->pluslist, al->dfs);
366         else
367             al->nminus -= PruneList(&al->minuslist, al->dfs);
368         return;
369     }
370     /* Otherwise we make a new item and plug in the new data. */
371     tlist = (struct AclEntry *) malloc(sizeof (struct AclEntry));
372     strcpy(tlist->name, aname);
373     tlist->rights = arights;
374     if (plus) {
375         tlist->next = al->pluslist;
376         al->pluslist = tlist;
377         al->nplus++;
378         if (arights == 0 || arights == -1)
379             al->nplus -= PruneList(&al->pluslist, al->dfs);
380     }
381     else {
382         tlist->next = al->minuslist;
383         al->minuslist = tlist;
384         al->nminus++;
385         if (arights == 0) al->nminus -= PruneList(&al->minuslist, al->dfs);
386     }
387 }
388
389 void ZapList (alist)
390     struct AclEntry *alist; {
391     register struct AclEntry *tp, *np;
392     for (tp = alist; tp; tp = np) {
393         np = tp->next;
394         free(tp);
395     }
396 }
397
398 int PruneList (ae, dfs)
399     struct AclEntry **ae;
400     int dfs;
401 {
402     struct AclEntry **lp;
403     struct AclEntry *te, *ne;
404     afs_int32 ctr;
405     ctr = 0;
406     lp = ae;
407     for(te = *ae;te;te=ne) {
408         if ((!dfs && te->rights == 0) || te->rights == -1) {
409             *lp = te->next;
410             ne = te->next;
411             free(te);
412             ctr++;
413         }
414         else {
415             ne = te->next;
416             lp = &te->next;
417         }
418     }
419     return ctr;
420 }
421
422 char *SkipLine (astr)
423     register char *astr; {
424     while (*astr !='\n') astr++;
425     astr++;
426     return astr;
427 }
428
429 /*
430  * Create an empty acl, taking into account whether the acl pointed
431  * to by astr is an AFS or DFS acl. Only parse this minimally, so we
432  * can recover from problems caused by bogus ACL's (in that case, always
433  * assume that the acl is AFS: for DFS, the user can always resort to
434  * acl_edit, but for AFS there may be no other way out).
435  */
436 struct Acl  *EmptyAcl(astr)
437     char *astr;
438 {
439     register struct Acl *tp;
440     int junk;
441
442     tp = (struct Acl *)malloc(sizeof (struct Acl));
443     tp->nplus = tp->nminus = 0;
444     tp->pluslist = tp->minuslist = 0;
445     tp->dfs = 0;
446     sscanf(astr, "%d dfs:%d %s", &junk, &tp->dfs, tp->cell);
447     return tp;
448 }
449
450 struct Acl *ParseAcl (astr)
451     char *astr; {
452     int nplus, nminus, i, trights;
453     char tname[MAXNAME];
454     struct AclEntry *first, *last, *tl;
455     struct Acl *ta;
456
457     ta = (struct Acl *) malloc (sizeof (struct Acl));
458     ta->dfs = 0;
459     sscanf(astr, "%d dfs:%d %s", &ta->nplus, &ta->dfs, ta->cell);
460     astr = SkipLine(astr);
461     sscanf(astr, "%d", &ta->nminus);
462     astr = SkipLine(astr);
463
464     nplus = ta->nplus;
465     nminus = ta->nminus;
466
467     last = 0;
468     first = 0;
469     for(i=0;i<nplus;i++) {
470         sscanf(astr, "%100s %d", tname, &trights);
471         astr = SkipLine(astr);
472         tl = (struct AclEntry *) malloc(sizeof (struct AclEntry));
473         if (!first) first = tl;
474         strcpy(tl->name, tname);
475         tl->rights = trights;
476         tl->next = 0;
477         if (last) last->next = tl;
478         last = tl;
479     }
480     ta->pluslist = first;
481
482     last = 0;
483     first = 0;
484     for(i=0;i<nminus;i++) {
485         sscanf(astr, "%100s %d", tname, &trights);
486         astr = SkipLine(astr);
487         tl = (struct AclEntry *) malloc(sizeof (struct AclEntry));
488         if (!first) first = tl;
489         strcpy(tl->name, tname);
490         tl->rights = trights;
491         tl->next = 0;
492         if (last) last->next = tl;
493         last = tl;
494     }
495     ta->minuslist = first;
496
497     return ta;
498 }
499
500 void PrintStatus(status, name, motd, offmsg)
501     VolumeStatus *status;
502     char *name;
503     char *motd;
504     char *offmsg; {
505     printf("Volume status for vid = %u named %s\n",status->Vid, name);
506     if (*offmsg != 0)
507         printf("Current offline message is %s\n",offmsg);
508     if (*motd != 0)
509         printf("Current message of the day is %s\n",motd);
510     printf("Current disk quota is ");
511     if (status->MaxQuota != 0) printf("%d\n", status->MaxQuota);
512     else printf("unlimited\n");
513     printf("Current blocks used are %d\n",status->BlocksInUse);
514     printf("The partition has %d blocks available out of %d\n\n",status->PartBlocksAvail, status->PartMaxBlocks);
515 }
516
517 void QuickPrintStatus(status, name)
518 VolumeStatus *status;
519 char *name; {
520     double QuotaUsed =0.0;
521     double PartUsed =0.0;
522     int WARN = 0;
523     printf("%-20s",name);
524
525     if (status->MaxQuota != 0) {
526         printf("%8d%8d", status->MaxQuota, status->BlocksInUse);
527         QuotaUsed = ((((double)status->BlocksInUse)/status->MaxQuota) * 100.0);
528     } else {
529         printf("no limit%8d", status->BlocksInUse);
530     }
531     if (QuotaUsed > 90.0){
532         printf(" %8.0f%%<<", QuotaUsed);
533         WARN = 1;
534     }
535     else printf(" %8.0f%%  ", QuotaUsed);
536     PartUsed = (100.0 - ((((double)status->PartBlocksAvail)/status->PartMaxBlocks) * 100.0));
537     if (PartUsed > 97.0){
538         printf(" %8.0f%%<<", PartUsed);
539         WARN = 1;
540     }
541     else printf(" %8.0f%%  ", PartUsed);
542     if (WARN){
543         printf("\t<<WARNING\n");
544     }
545     else printf("\n");
546 }
547
548 void QuickPrintSpace(status, name)
549 VolumeStatus *status;
550 char *name; {
551     double PartUsed =0.0;
552     int WARN = 0;
553     printf("%-20s",name);
554
555     printf("%8d%8d%8d", status->PartMaxBlocks, status->PartMaxBlocks - status->PartBlocksAvail, status->PartBlocksAvail);
556         
557     PartUsed = (100.0 - ((((double)status->PartBlocksAvail)/status->PartMaxBlocks) * 100.0));
558     if (PartUsed > 90.0){
559         printf(" %8.0f%%<<", PartUsed);
560         WARN = 1;
561     }
562     else printf(" %8.0f%%  ", PartUsed);
563     if (WARN){
564         printf("\t<<WARNING\n");
565     }
566     else printf("\n");
567 }
568
569 char *AclToString(acl)
570     struct Acl *acl; {
571     static char mydata[MAXSIZE];
572     char tstring[MAXSIZE];
573     char dfsstring[30];
574     struct AclEntry *tp;
575     
576     if (acl->dfs) sprintf(dfsstring, " dfs:%d %s", acl->dfs, acl->cell);
577     else dfsstring[0] = '\0';
578     sprintf(mydata, "%d%s\n%d\n", acl->nplus, dfsstring, acl->nminus);
579     for(tp = acl->pluslist;tp;tp=tp->next) {
580         sprintf(tstring, "%s %d\n", tp->name, tp->rights);
581         strcat(mydata, tstring);
582     }
583     for(tp = acl->minuslist;tp;tp=tp->next) {
584         sprintf(tstring, "%s %d\n", tp->name, tp->rights);
585         strcat(mydata, tstring);
586     }
587     return mydata;
588 }
589
590 static SetACLCmd(as)
591 struct cmd_syndesc *as; {
592     register afs_int32 code;
593     struct ViceIoctl blob;
594     struct Acl *ta;
595     register struct cmd_item *ti, *ui;
596     int plusp;
597     afs_int32 rights;
598     int clear;
599     int idf = getidf(as, parm_setacl_id);
600
601     if (as->parms[2].items) clear=1;
602     else clear=0;
603     plusp = !(as->parms[3].items);
604     for(ti=as->parms[0].items; ti;ti=ti->next) {
605         blob.out_size = MAXSIZE;
606         blob.in_size = idf;
607         blob.in = blob.out = space;
608         code = pioctl(ti->data, VIOCGETAL, &blob, 1);
609         if (code) {
610             Die(errno, ti->data);
611             return 1;
612         }
613         ta = ParseAcl(space);
614         if (!plusp && ta->dfs) {
615             fprintf(stderr,
616                     "fs: %s: you may not use the -negative switch with DFS acl's.\n%s",
617                     ti->data,
618                     "(you may specify \"null\" to revoke all rights, however)\n");
619             return 1;
620         }
621         if (clear) ta = EmptyAcl(space);
622         else ta = ParseAcl(space);
623         CleanAcl(ta);
624         for(ui=as->parms[1].items; ui; ui=ui->next->next) {
625             enum rtype rtype;
626             if (!ui->next) {
627                 fprintf(stderr,"%s: Missing second half of user/access pair.\n", pn);
628                 return 1;
629             }
630             rights = Convert(ui->next->data, ta->dfs, &rtype);
631             if (rtype == destroy && !ta->dfs) {
632                 if (!FindList(ta->pluslist, ui->data)) {
633                     fprintf(stderr,"%s: Invalid arg (%s doesn't exist in the current acl)\n", pn, ui->data);
634                     return 1;
635                 }
636             }
637             if (rtype == deny && !ta->dfs) plusp = 0;
638             if (rtype == destroy && ta->dfs) rights = -1;
639             ChangeList(ta, plusp, ui->data, rights);
640         }
641         blob.in = AclToString(ta);
642         blob.out_size=0;
643         blob.in_size = 1+strlen(blob.in);
644         code = pioctl(ti->data, VIOCSETAL, &blob, 1);
645         if (code) {
646             if (errno == EINVAL) {
647                 if (ta->dfs) {
648                     static char *fsenv = 0;
649                     if (!fsenv) {
650                         fsenv = (char *)getenv("FS_EXPERT");
651                     }
652                     fprintf(stderr, "fs: \"Invalid argument\" was returned when you tried to store a DFS access list.\n");
653                     if (!fsenv) {
654                         fprintf(stderr,
655     "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
656     "\nPossible reasons for this include:\n\n",                     
657     " -You may have specified an inappropriate combination of rights.\n",
658     "  For example, some DFS-supported filesystems may not allow you to\n",
659     "  drop the \"c\" right from \"user_obj\".\n\n",
660     " -A mask_obj may be required (it is likely required by the underlying\n",
661     "  filesystem if you try to set anything other than the basic \"user_obj\"\n",
662     "  \"mask_obj\", or \"group_obj\" entries). Unlike acl_edit, the fs command\n",
663     "  does not automatically create or update the mask_obj. Try setting\n",
664     "  the rights \"mask_obj all\" with \"fs sa\" before adding any explicit\n",
665     "  users or groups. You can do this with a single command, such as\n",
666     "  \"fs sa mask_obj all user:somename read\"\n\n",
667     " -A specified user or group may not exist.\n\n",
668     " -You may have tried to delete \"user_obj\", \"group_obj\", or \"other_obj\".\n",
669     "  This is probably not allowed by the underlying file system.\n\n",
670     " -If you add a user or group to a DFS ACL, remember that it must be\n",
671     "  fully specified as \"user:username\" or \"group:groupname\". In addition, there\n",
672     "  may be local requirements on the format of the user or group name.\n",
673     "  Check with your cell administrator.\n\n",                            
674     " -Or numerous other possibilities. It would be great if we could be more\n",
675     "  precise about the actual problem, but for various reasons, this is\n",
676     "  impractical via this interface.  If you can't figure it out, you\n",
677     "  might try logging into a DCE-equipped machine and use acl_edit (or\n",
678     "  whatever is provided). You may get better results. Good luck!\n\n",
679     " (You may inhibit this message by setting \"FS_EXPERT\" in your environment)\n");
680                     }
681                 } else {
682                     fprintf(stderr,"%s: Invalid argument, possible reasons include:\n", pn);
683                     fprintf(stderr,"\t-File not in AFS\n");
684                     fprintf(stderr,"\t-Too many users on access control list\n");
685                     fprintf(stderr,"\t-Tried to add non-existent user to access control list\n");
686                 }
687                 return 1;
688             }
689             else {
690                 Die(errno, ti->data);
691                 continue;
692             }
693         }
694     }
695     return 0;
696 }
697
698
699 static CopyACLCmd(as)
700 struct cmd_syndesc *as; {
701     register afs_int32 code;
702     struct ViceIoctl blob;
703     struct Acl *fa, *ta;
704     struct AclEntry *tp;
705     register struct cmd_item *ti;
706     int clear;
707     int idf = getidf(as, parm_copyacl_id);
708
709     if (as->parms[2].items) clear=1;
710     else clear=0;
711     blob.out_size = MAXSIZE;
712     blob.in_size = idf;
713     blob.in = blob.out = space;
714     code = pioctl(as->parms[0].items->data, VIOCGETAL, &blob, 1);
715     if (code) {
716         Die(errno, as->parms[0].items->data);
717         return 1;
718     }
719     fa = ParseAcl(space);
720     CleanAcl(fa);
721     for (ti=as->parms[1].items; ti;ti=ti->next) {
722         blob.out_size = MAXSIZE;
723         blob.in_size = idf;
724         blob.in = blob.out = space;
725         code = pioctl(ti->data, VIOCGETAL, &blob, 1);
726         if (code) {
727             Die(errno, ti->data);
728             return 1;
729         }
730         if (clear) ta = EmptyAcl(space);
731         else ta = ParseAcl(space);
732         CleanAcl(ta);
733         if (ta->dfs != fa->dfs) {
734             fprintf(stderr, "fs: incompatible file system types: acl not copied to %s; aborted\n", ti->data);
735             goto next;
736         }
737         if (ta->dfs) {
738             if (! clear && strcmp(ta->cell, fa->cell) != 0) {
739                 fprintf(stderr, "fs: default DCE cell differs for file %s: use \"-clear\" switch; acl not merged\n", ti->data);
740                 goto next;
741             }
742             strcpy(ta->cell, fa->cell);
743         }
744         for (tp = fa->pluslist;tp;tp=tp->next) 
745             ChangeList(ta, 1, tp->name, tp->rights);
746         for (tp = fa->minuslist;tp;tp=tp->next) 
747             ChangeList(ta, 0, tp->name, tp->rights);
748         blob.in = AclToString(ta);
749         blob.out_size=0;
750         blob.in_size = 1+strlen(blob.in);
751         code = pioctl(ti->data, VIOCSETAL, &blob, 1);
752         if (code) {
753             if (errno == EINVAL) {
754                 fprintf(stderr,"%s: Invalid argument, possible reasons include:\n", pn);
755                 fprintf(stderr,"\t-File not in AFS\n");
756                 return 1;
757             }
758             else {
759                 Die(errno, ti->data);
760                 goto next;
761             }
762         }
763       next:
764         ZapAcl(ta);
765     }
766     return 0;
767 }
768
769
770
771 /* tell if a name is 23 or -45 (digits or minus digits), which are bad names we must prune */
772 static BadName(aname)
773 register char *aname; {
774     register int tc;
775     while(tc = *aname++) {
776         /* all must be '-' or digit to be bad */
777         if (tc != '-' && (tc < '0' || tc > '9')) return 0;
778     }
779     return 1;
780 }
781
782
783 /* clean up an access control list of its bad entries; return 1 if we made
784    any changes to the list, and 0 otherwise */
785 int CleanAcl(aa)
786 struct Acl *aa; {
787     register struct AclEntry *te, **le, *ne;
788     int changes;
789
790     /* Don't correct DFS ACL's for now */
791     if (aa->dfs)
792         return 0;
793
794     /* prune out bad entries */
795     changes = 0;            /* count deleted entries */
796     le = &aa->pluslist;
797     for(te = aa->pluslist; te; te=ne) {
798         ne = te->next;
799         if (BadName(te->name)) {
800             /* zap this dude */
801             *le = te->next;
802             aa->nplus--;
803             free(te);
804             changes++;
805         }
806         else {
807             le = &te->next;
808         }
809     }
810     le = &aa->minuslist;
811     for(te = aa->minuslist; te; te=ne) {
812         ne = te->next;
813         if (BadName(te->name)) {
814             /* zap this dude */
815             *le = te->next;
816             aa->nminus--;
817             free(te);
818             changes++;
819         }
820         else {
821             le = &te->next;
822         }
823     }
824     return changes;
825 }
826
827
828 /* clean up an acl to not have bogus entries */
829 static CleanACLCmd(as)
830 register struct cmd_syndesc *as; {
831     register afs_int32 code;
832     register struct Acl *ta;
833     struct ViceIoctl blob;
834     int changes;
835     register struct cmd_item *ti;
836     register struct AclEntry *te;
837
838     SetDotDefault(&as->parms[0].items);
839     for(ti=as->parms[0].items; ti; ti=ti->next) {
840         blob.out_size = MAXSIZE;
841         blob.in_size = 0;
842         blob.out = space;
843         code = pioctl(ti->data, VIOCGETAL, &blob, 1);
844         if (code) {
845             Die(errno, ti->data);
846             continue;
847         }
848         ta = ParseAcl(space);
849
850         if (ta->dfs) {
851             fprintf(stderr,
852                     "%s: cleanacl is not supported for DFS access lists.\n",
853                     pn);
854             return 1;
855         }
856
857         changes = CleanAcl(ta);
858
859         if (changes) {
860             /* now set the acl */
861             blob.in=AclToString(ta);
862             blob.in_size = strlen(blob.in)+1;
863             blob.out_size = 0;
864             code = pioctl(ti->data, VIOCSETAL, &blob, 1);
865             if (code) {
866                 if (errno == EINVAL) {
867                     fprintf(stderr,"%s: Invalid argument, possible reasons include\n", pn);
868                     fprintf(stderr,"%s: File not in vice or\n", pn);
869                     fprintf(stderr,"%s: Too many users on access control list or\n", pn);
870                     return 1;
871                 }
872                 else {
873                     Die(errno, ti->data);
874                     continue;
875                 }
876             }
877
878             /* now list the updated acl */
879             printf("Access list for %s is now\n", ti->data);
880             if (ta->nplus > 0) {
881                 if (!ta->dfs) printf("Normal rights:\n");
882                 for(te = ta->pluslist;te;te=te->next) {
883                     printf("  %s ", te->name);
884                     PRights(te->rights, ta->dfs);
885                     printf("\n");
886                 }
887             }
888             if (ta->nminus > 0) {
889                 printf("Negative rights:\n");
890                 for(te = ta->minuslist;te;te=te->next) {
891                     printf("  %s ", te->name);
892                     PRights(te->rights, ta->dfs);
893                     printf("\n");
894                 }
895             }
896             if (ti->next) printf("\n");
897         }
898         else
899             printf("Access list for %s is fine.\n", ti->data);
900     }
901     return 0;
902 }
903
904 static ListACLCmd(as)
905 register struct cmd_syndesc *as; {
906     register afs_int32 code;
907     register struct Acl *ta;
908     struct ViceIoctl blob;
909     struct AclEntry *te;
910     register struct cmd_item *ti;
911     int idf = getidf(as, parm_listacl_id);
912
913     SetDotDefault(&as->parms[0].items);
914     for(ti=as->parms[0].items; ti; ti=ti->next) {
915         char separator;
916         blob.out_size = MAXSIZE;
917         blob.in_size = idf;
918         blob.in = blob.out = space;
919         code = pioctl(ti->data, VIOCGETAL, &blob, 1);
920         if (code) {
921             Die(errno, ti->data);
922             continue;
923         }
924         ta = ParseAcl(space);
925         switch (ta->dfs) {
926           case 0:
927             printf("Access list for %s is\n", ti->data);
928             break;
929           case 1:
930             printf("DFS access list for %s is\n", ti->data);
931             break;
932           case 2:
933             printf("DFS initial directory access list of %s is\n", ti->data);
934             break;
935           case 3:
936             printf("DFS initial file access list of %s is\n", ti->data);
937             break;
938         }
939         if (ta->dfs) {
940             printf("  Default cell = %s\n", ta->cell);
941         }
942         separator = ta->dfs? DFS_SEPARATOR : ' ';
943         if (ta->nplus > 0) {
944             if (!ta->dfs) printf("Normal rights:\n");
945             for(te = ta->pluslist;te;te=te->next) {
946                 printf("  %s%c", te->name, separator);
947                 PRights(te->rights, ta->dfs);
948                 printf("\n");
949             }
950         }
951         if (ta->nminus > 0) {
952             printf("Negative rights:\n");
953             for(te = ta->minuslist;te;te=te->next) {
954                 printf("  %s ", te->name);
955                 PRights(te->rights, ta->dfs);
956                 printf("\n");
957             }
958         }
959         if (ti->next) printf("\n");
960     }
961     return 0;
962 }
963
964 static FlushVolumeCmd(as)
965 register struct cmd_syndesc *as; {
966     register afs_int32 code;
967     struct ViceIoctl blob;
968     register struct cmd_item *ti;
969
970     for(ti=as->parms[0].items; ti; ti=ti->next) {
971         blob.in_size = blob.out_size = 0;
972         code = pioctl(ti->data, VIOC_FLUSHVOLUME, &blob, 0);
973         if (code) {
974             fprintf(stderr, "Error flushing volume\n");
975             Die(errno,ti->data);
976             continue;
977         }
978     }
979     return 0;
980 }
981
982 static FlushCmd(as)
983 register struct cmd_syndesc *as; {
984     register afs_int32 code;
985     struct ViceIoctl blob;
986     int error;
987     register struct cmd_item *ti;
988
989     error = 0;
990     for(ti=as->parms[0].items; ti; ti=ti->next) {
991         blob.in_size = blob.out_size = 0;
992         code = pioctl(ti->data, VIOCFLUSH, &blob, 0);
993         if (code) {
994             error = 1;
995             if (errno == EMFILE) {
996                 fprintf(stderr,"%s: Can't flush active file %s\n", pn, ti->data);
997                 continue;
998             }
999             else {
1000                 fprintf(stderr, "Error flushing file\n");
1001                 Die(errno,ti->data);
1002                 continue;
1003             }
1004         }
1005     }
1006     return error;
1007 }
1008
1009 /* all this command does is repackage its args and call SetVolCmd */
1010 static SetQuotaCmd(as)
1011 register struct cmd_syndesc *as; {
1012     struct cmd_syndesc ts;
1013
1014     /* copy useful stuff from our command slot; we may later have to reorder */
1015     memcpy(&ts, as, sizeof(ts));        /* copy whole thing */
1016     return SetVolCmd(&ts);
1017 }
1018
1019 static SetVolCmd(as)
1020 register struct cmd_syndesc *as; {
1021     register afs_int32 code;
1022     struct ViceIoctl blob;
1023     register struct cmd_item *ti;
1024     struct VolumeStatus *status;
1025     char *motd, *offmsg, *input;
1026
1027     SetDotDefault(&as->parms[0].items);
1028     for(ti=as->parms[0].items; ti; ti=ti->next) {
1029         /* once per file */
1030         blob.out_size = MAXSIZE;
1031         blob.in_size = sizeof(*status) + 3;     /* for the three terminating nulls */
1032         blob.out = space;
1033         blob.in = space;
1034         status = (VolumeStatus *)space;
1035         status->MinQuota = status->MaxQuota = -1;
1036         motd = offmsg = (char *) 0;
1037         if (as->parms[1].items) {
1038             code = util_GetInt32(as->parms[1].items->data, &status->MaxQuota);
1039             if (code) {
1040                 fprintf(stderr,"fs: bad integer specified for quota.\n");
1041                 return code;
1042             }
1043         }
1044         if (as->parms[2].items) motd = as->parms[2].items->data;
1045         if (as->parms[3].items) offmsg = as->parms[3].items->data;
1046         input = (char *)status + sizeof(*status);
1047         *(input++) = '\0';      /* never set name: this call doesn't change vldb */
1048         if(offmsg) {
1049           if (strlen(offmsg) >= VMSGSIZE) {
1050             fprintf(stderr,"fs: message must be shorter than %d characters\n",
1051                     VMSGSIZE);
1052             return code;
1053           }
1054             strcpy(input,offmsg);
1055             blob.in_size += strlen(offmsg);
1056             input += strlen(offmsg) + 1;
1057         }
1058         else *(input++) = '\0';
1059         if(motd) {
1060           if (strlen(motd) >= VMSGSIZE) {
1061             fprintf(stderr,"fs: message must be shorter than %d characters\n",
1062                     VMSGSIZE);
1063             return code;
1064           }
1065             strcpy(input,motd);
1066             blob.in_size += strlen(motd);
1067             input += strlen(motd) + 1;
1068         }
1069         else *(input++) = '\0';
1070         code = pioctl(ti->data,VIOCSETVOLSTAT, &blob, 1);
1071         if (code) {
1072             Die(errno, ti->data);
1073             return 1;
1074         }
1075     }
1076     return 0;
1077 }
1078
1079 static ExamineCmd(as)
1080 register struct cmd_syndesc *as; {
1081     register afs_int32 code;
1082     struct ViceIoctl blob;
1083     register struct cmd_item *ti;
1084     struct VolumeStatus *status;
1085     char *name, *offmsg, *motd;
1086     int error;
1087     
1088     error = 0;
1089     SetDotDefault(&as->parms[0].items);
1090     for(ti=as->parms[0].items; ti; ti=ti->next) {
1091         /* once per file */
1092         blob.out_size = MAXSIZE;
1093         blob.in_size = 0;
1094         blob.out = space;
1095         code = pioctl(ti->data, VIOCGETVOLSTAT, &blob, 1);
1096         if (code) {
1097             Die(errno, ti->data);
1098             error = 1;
1099             continue;
1100         }
1101         status = (VolumeStatus *)space;
1102         name = (char *)status + sizeof(*status);
1103         offmsg = name + strlen(name) + 1;
1104         motd = offmsg + strlen(offmsg) + 1;
1105         PrintStatus(status, name, motd, offmsg);
1106     }
1107     return error;
1108 }
1109
1110 static ListQuotaCmd(as)
1111 register struct cmd_syndesc *as; {
1112     register afs_int32 code;
1113     struct ViceIoctl blob;
1114     register struct cmd_item *ti;
1115     struct VolumeStatus *status;
1116     char *name;
1117     
1118     printf("%-20s%-9s%-8s%-11s%-11s\n",
1119             "Volume Name","   Quota", "   Used", "   % Used", " Partition");
1120     SetDotDefault(&as->parms[0].items);
1121     for(ti=as->parms[0].items; ti; ti=ti->next) {
1122         /* once per file */
1123         blob.out_size = MAXSIZE;
1124         blob.in_size = 0;
1125         blob.out = space;
1126         code = pioctl(ti->data, VIOCGETVOLSTAT, &blob, 1);
1127         if (code) {
1128             Die(errno, ti->data);
1129             continue;
1130         }
1131         status = (VolumeStatus *)space;
1132         name = (char *)status + sizeof(*status);
1133         QuickPrintStatus(status, name);
1134     }
1135     return 0;
1136 }
1137
1138 static WhereIsCmd(as)
1139 register struct cmd_syndesc *as; {
1140     register afs_int32 code;
1141     struct ViceIoctl blob;
1142     register struct cmd_item *ti;
1143     register int j;
1144     afs_int32 *hosts;
1145     char *tp;
1146     
1147     SetDotDefault(&as->parms[0].items);
1148     for(ti=as->parms[0].items; ti; ti=ti->next) {
1149         /* once per file */
1150         blob.out_size = MAXSIZE;
1151         blob.in_size = 0;
1152         blob.out = space;
1153         memset(space, 0, sizeof(space));
1154         code = pioctl(ti->data, VIOCWHEREIS, &blob, 1);
1155         if (code) {
1156             Die(errno, ti->data);
1157             continue;
1158         }
1159         hosts = (afs_int32 *) space;
1160         printf("File %s is on host%s ", ti->data, (hosts[0] && !hosts[1]) ? "": "s");
1161         for(j=0; j<MAXHOSTS; j++) {
1162             if (hosts[j] == 0) break;
1163             tp = hostutil_GetNameByINet(hosts[j]);
1164             printf("%s ", tp);
1165         }
1166         printf("\n");
1167     }
1168     return 0;
1169 }
1170
1171
1172 static DiskFreeCmd(as)
1173 register struct cmd_syndesc *as; {
1174     register afs_int32 code;
1175     struct ViceIoctl blob;
1176     register struct cmd_item *ti;
1177     char *name;
1178     struct VolumeStatus *status;
1179     
1180     printf("%-20s%-9s%-8s%-11s%-11s\n",
1181             "Volume Name","  kbytes ", " used", "  avail ", " %used");
1182     SetDotDefault(&as->parms[0].items);
1183     for(ti=as->parms[0].items; ti; ti=ti->next) {
1184         /* once per file */
1185         blob.out_size = MAXSIZE;
1186         blob.in_size = 0;
1187         blob.out = space;
1188         code = pioctl(ti->data, VIOCGETVOLSTAT, &blob, 1);
1189         if (code) {
1190             Die(errno, ti->data);
1191             continue;
1192         }
1193         status = (VolumeStatus *)space;
1194         name = (char *)status + sizeof(*status);
1195         QuickPrintSpace(status, name);
1196     }
1197     return 0;
1198 }
1199
1200 static QuotaCmd(as)
1201 register struct cmd_syndesc *as; {
1202     register afs_int32 code;
1203     struct ViceIoctl blob;
1204     register struct cmd_item *ti;
1205     double quotaPct;
1206     struct VolumeStatus *status;
1207     
1208     SetDotDefault(&as->parms[0].items);
1209     for(ti=as->parms[0].items; ti; ti=ti->next) {
1210         /* once per file */
1211         blob.out_size = MAXSIZE;
1212         blob.in_size = 0;
1213         blob.out = space;
1214         code = pioctl(ti->data, VIOCGETVOLSTAT, &blob, 1);
1215         if (code) {
1216             Die(errno, ti->data);
1217             continue;
1218         }
1219         status = (VolumeStatus *)space;
1220         if (status->MaxQuota) quotaPct = ((((double)status->BlocksInUse)/status->MaxQuota) * 100.0);
1221         else quotaPct = 0.0;
1222         printf("%2.0f%% of quota used.\n", quotaPct);
1223     }
1224     return 0;
1225 }
1226
1227 static ListMountCmd(as)
1228 register struct cmd_syndesc *as; {
1229     register afs_int32 code;
1230     struct ViceIoctl blob;
1231     int error;
1232     register struct cmd_item *ti;
1233     char orig_name[1024];               /*Original name, may be modified*/
1234     char true_name[1024];               /*``True'' dirname (e.g., symlink target)*/
1235     char parent_dir[1024];              /*Parent directory of true name*/
1236     register char *last_component;      /*Last component of true name*/
1237 #ifndef WIN32
1238     struct stat statbuff;               /*Buffer for status info*/
1239 #endif /* not WIN32 */
1240 #ifndef WIN32
1241     int link_chars_read;                /*Num chars read in readlink()*/
1242 #endif /* not WIN32 */
1243     int thru_symlink;                   /*Did we get to a mount point via a symlink?*/
1244     
1245     error = 0;
1246     for(ti=as->parms[0].items; ti; ti=ti->next) {
1247         /* once per file */
1248         thru_symlink = 0;
1249 #ifdef WIN32
1250         strcpy(orig_name, ti->data);
1251 #else /* not WIN32 */
1252         sprintf(orig_name, "%s%s",
1253                 (ti->data[0] == '/') ? "" : "./",
1254                 ti->data);
1255 #endif /* not WIN32 */
1256
1257 #ifndef WIN32
1258         if (lstat(orig_name, &statbuff) < 0) {
1259             /* if lstat fails, we should still try the pioctl, since it
1260                 may work (for example, lstat will fail, but pioctl will
1261                     work if the volume of offline (returning ENODEV). */
1262             statbuff.st_mode = S_IFDIR; /* lie like pros */
1263         }
1264
1265         /*
1266          * The lstat succeeded.  If the given file is a symlink, substitute
1267          * the file name with the link name.
1268          */
1269         if ((statbuff.st_mode & S_IFMT) == S_IFLNK) {
1270             thru_symlink = 1;
1271             /*
1272              * Read name of resolved file.
1273              */
1274             link_chars_read = readlink(orig_name, true_name, 1024);
1275             if (link_chars_read <= 0) {
1276                 fprintf(stderr,"%s: Can't read target name for '%s' symbolic link!\n",
1277                        pn, orig_name);
1278                 exit(1);
1279             }
1280
1281             /*
1282              * Add a trailing null to what was read, bump the length.
1283              */
1284             true_name[link_chars_read++] = 0;
1285
1286             /*
1287              * If the symlink is an absolute pathname, we're fine.  Otherwise, we
1288              * have to create a full pathname using the original name and the
1289              * relative symlink name.  Find the rightmost slash in the original
1290              * name (we know there is one) and splice in the symlink value.
1291              */
1292             if (true_name[0] != '\\') {
1293                 last_component = (char *) strrchr(orig_name, '\\');
1294                 strcpy(++last_component, true_name);
1295                 strcpy(true_name, orig_name);
1296             }
1297         }
1298         else
1299             strcpy(true_name, orig_name);
1300 #else   /* WIN32 */
1301         strcpy(true_name, orig_name);
1302 #endif /* WIN32 */
1303
1304         /*
1305          * Find rightmost slash, if any.
1306          */
1307         last_component = (char *) strrchr(true_name, '\\');
1308         if (!last_component)
1309             last_component = (char *) strrchr(true_name, '/');
1310         if (last_component) {
1311             /*
1312              * Found it.  Designate everything before it as the parent directory,
1313              * everything after it as the final component.
1314              */
1315             strncpy(parent_dir, true_name, last_component - true_name + 1);
1316             parent_dir[last_component - true_name + 1] = 0;
1317             last_component++;   /*Skip the slash*/
1318         }
1319         else {
1320             /*
1321              * No slash appears in the given file name.  Set parent_dir to the current
1322              * directory, and the last component as the given name.
1323              */
1324             fs_ExtractDriveLetter(true_name, parent_dir);
1325             strcat(parent_dir, ".");
1326             last_component = true_name;
1327             fs_StripDriveLetter(true_name, true_name, sizeof(true_name));
1328         }
1329
1330         if (strcmp(last_component, ".") == 0 || strcmp(last_component, "..") == 0) {
1331             fprintf(stderr,"fs: you may not use '.' or '..' as the last component\n");
1332             fprintf(stderr,"fs: of a name in the 'fs lsmount' command.\n");
1333             error = 1;
1334             continue;
1335         }
1336
1337         blob.in = last_component;
1338         blob.in_size = strlen(last_component)+1;
1339         blob.out_size = MAXSIZE;
1340         blob.out = space;
1341         memset(space, 0, MAXSIZE);
1342
1343         code = pioctl(parent_dir, VIOC_AFS_STAT_MT_PT, &blob, 1);
1344
1345         if (code == 0)
1346             printf("'%s' is a %smount point for volume '%s'\n",
1347                    ti->data,
1348                    (thru_symlink ? "symbolic link, leading to a " : ""),
1349                    space);
1350
1351         else {
1352             error = 1;
1353             if (errno == EINVAL)
1354                 fprintf(stderr,"'%s' is not a mount point.\n",
1355                        ti->data);
1356             else {
1357                 Die(errno, (ti->data ? ti->data : parent_dir));
1358             }
1359         }
1360     }
1361     return error;
1362 }
1363
1364 static MakeMountCmd(as)
1365 register struct cmd_syndesc *as; {
1366     register afs_int32 code;
1367     register char *cellName, *volName, *tmpName;
1368 #ifdef WIN32
1369     char localCellName[1000];
1370 #else /* not WIN32 */
1371     struct afsconf_cell info;
1372     struct vldbentry vldbEntry;
1373 #endif /* not WIN32 */
1374     struct ViceIoctl blob;
1375
1376 /*
1377
1378 defect #3069
1379
1380     if (as->parms[5].items && !as->parms[2].items) {
1381         fprintf(stderr,"fs: must provide cell when creating cellular mount point.\n");
1382         return 1;
1383     }
1384 */
1385
1386     if (as->parms[2].items)     /* cell name specified */
1387         cellName = as->parms[2].items->data;
1388     else
1389         cellName = (char *) 0;
1390     volName = as->parms[1].items->data;
1391
1392     if (strlen(volName) >= 64) {
1393         fprintf(stderr,"fs: volume name too long (length must be < 64 characters)\n");
1394         return 1;
1395     }
1396
1397     /* Check for a cellname in the volume specification, and complain
1398      * if it doesn't match what was specified with -cell */
1399     if (tmpName = strchr(volName, ':')) {
1400       *tmpName = '\0';
1401       if (cellName) {
1402         if (foldcmp(cellName,volName)) {
1403           fprintf(stderr,"fs: cellnames do not match.\n");
1404           return 1;
1405         }
1406       }
1407       cellName = volName;
1408       volName = ++tmpName;
1409     }
1410
1411     if (!InAFS(Parent(as->parms[0].items->data))) {
1412         fprintf(stderr,"fs: mount points must be created within the AFS file system\n");
1413         return 1;
1414     }
1415
1416     if (!cellName) {
1417         blob.in_size = 0;
1418         blob.out_size = MAXSIZE;
1419         blob.out = space;
1420         code = pioctl(Parent(as->parms[0].items->data), VIOC_FILE_CELL_NAME, &blob, 1);
1421     }
1422
1423 #ifdef WIN32
1424     strcpy(localCellName, (cellName? cellName : space));
1425 #else /* not win32 */
1426     code = GetCellName(cellName?cellName:space, &info);
1427     if (code) {
1428         return 1;
1429     }
1430     if (!(as->parms[4].items)) {
1431       /* not fast, check which cell the mountpoint is being created in */
1432       code = 0;
1433         /* not fast, check name with VLDB */
1434       if (!code)
1435         code = VLDBInit(1, &info);
1436       if (code == 0) {
1437           /* make the check.  Don't complain if there are problems with init */
1438           code = ubik_Call(VL_GetEntryByNameO, uclient, 0, volName, &vldbEntry);
1439           if (code == VL_NOENT) {
1440               fprintf(stderr,"fs: warning, volume %s does not exist in cell %s.\n",
1441                       volName, cellName ? cellName : space);
1442           }
1443       }
1444     }
1445 #endif /* not WIN32 */
1446
1447     if (as->parms[3].items)     /* if -rw specified */
1448         strcpy(space, "%");
1449     else
1450         strcpy(space, "#");
1451     if (cellName) {
1452         /* cellular mount point, prepend cell prefix */
1453 #ifdef WIN32
1454         strcat(space, localCellName);
1455 #else /* not WIN32 */
1456         strcat(space, info.name);
1457 #endif /* not WIN32 */
1458         strcat(space, ":");
1459     }
1460     strcat(space, volName);     /* append volume name */
1461     strcat(space, ".");         /* stupid convention; these end with a period */
1462 #ifdef WIN32
1463     /* create symlink with a special pioctl for Windows NT, since it doesn't
1464      * have a symlink system call.
1465      */
1466     blob.out_size = 0;
1467     blob.in_size = 1 + strlen(space);
1468     blob.in = space;
1469     blob.out = NULL;
1470     code = pioctl(as->parms[0].items->data, VIOC_AFS_CREATE_MT_PT, &blob, 0);
1471 #else /* not WIN32 */
1472     code = symlink(space, as->parms[0].items->data);
1473 #endif /* not WIN32 */
1474     if (code) {
1475         Die(errno, as->parms[0].items->data);
1476         return 1;
1477     }
1478     return 0;
1479 }
1480
1481 /*
1482  * Delete AFS mount points.  Variables are used as follows:
1483  *       tbuffer: Set to point to the null-terminated directory name of the mount point
1484  *          (or ``.'' if none is provided)
1485  *      tp: Set to point to the actual name of the mount point to nuke.
1486  */
1487 static RemoveMountCmd(as)
1488 register struct cmd_syndesc *as; {
1489     register afs_int32 code=0;
1490     struct ViceIoctl blob;
1491     register struct cmd_item *ti;
1492     char tbuffer[1024];
1493     char lsbuffer[1024];
1494     register char *tp;
1495     
1496     for(ti=as->parms[0].items; ti; ti=ti->next) {
1497         /* once per file */
1498         tp = (char *) strrchr(ti->data, '\\');
1499         if (!tp)
1500             tp = (char *) strrchr(ti->data, '/');
1501         if (tp) {
1502             strncpy(tbuffer, ti->data, code=tp-ti->data+1);  /* the dir name */
1503             tbuffer[code] = 0;
1504             tp++;   /* skip the slash */
1505         }
1506         else {
1507             fs_ExtractDriveLetter(ti->data, tbuffer);
1508             strcat(tbuffer, ".");
1509             tp = ti->data;
1510             fs_StripDriveLetter(tp, tp, 0);
1511         }
1512         blob.in = tp;
1513         blob.in_size = strlen(tp)+1;
1514         blob.out = lsbuffer;
1515         blob.out_size = sizeof(lsbuffer);
1516         code = pioctl(tbuffer, VIOC_AFS_STAT_MT_PT, &blob, 0);
1517         if (code) {
1518             if (errno == EINVAL)
1519                 fprintf(stderr,"fs: '%s' is not a mount point.\n", ti->data);
1520             else {
1521                 Die(errno, ti->data);
1522             }
1523             continue;   /* don't bother trying */
1524         }
1525         blob.out_size = 0;
1526         blob.in = tp;
1527         blob.in_size = strlen(tp)+1;
1528         code = pioctl(tbuffer, VIOC_AFS_DELETE_MT_PT, &blob, 0);
1529         if (code) {
1530             Die(errno, ti->data);
1531         }
1532
1533     }
1534     return code;
1535 }
1536
1537 /*
1538 */
1539
1540 static CheckServersCmd(as)
1541 register struct cmd_syndesc *as; {
1542     register afs_int32 code;
1543     struct ViceIoctl blob;
1544     register afs_int32 j;
1545     afs_int32 temp;
1546     register char *tp;
1547     struct afsconf_cell info;
1548     struct chservinfo checkserv;
1549
1550     memset(&checkserv, 0, sizeof(struct chservinfo));
1551     blob.in_size=sizeof(struct chservinfo);
1552     blob.in=(caddr_t)&checkserv;
1553
1554     blob.out_size = MAXSIZE;
1555     blob.out = space;
1556     memset(space, 0, sizeof(afs_int32));        /* so we assure zero when nothing is copied back */
1557
1558     /* prepare flags for checkservers command */
1559     temp = 2;   /* default to checking local cell only */
1560     if (as->parms[2].items) temp |= 1;  /* set fast flag */
1561     if (as->parms[1].items) temp &= ~2; /* turn off local cell check */
1562     
1563     checkserv.magic = 0x12345678;       /* XXX */
1564     checkserv.tflags=temp;
1565
1566     /* now copy in optional cell name, if specified */
1567     if (as->parms[0].items) {
1568         code = GetCellName(as->parms[0].items->data, &info);
1569         if (code) {
1570             return 1;
1571         }
1572         strcpy(checkserv.tbuffer,info.name);
1573         checkserv.tsize=strlen(info.name)+1;
1574     }
1575         else
1576         {
1577                 strcpy(checkserv.tbuffer,"\0");
1578                 checkserv.tsize=0;
1579         }
1580
1581         if(as->parms[3].items)
1582         {
1583                 checkserv.tinterval=atol(as->parms[3].items->data);
1584
1585                 /* sanity check */
1586                 if(checkserv.tinterval<0) {
1587                     printf("Warning: The negative -interval is ignored; treated as an inquiry\n");
1588                     checkserv.tinterval=0;
1589                 } else if(checkserv.tinterval> 600) {
1590                     printf("Warning: The maximum -interval value is 10 mins (600 secs)\n");
1591                     checkserv.tinterval=600;    /* 10 min max interval */
1592                }
1593         }
1594         else {
1595           checkserv.tinterval = -1;     /* don't change current interval */
1596         }
1597
1598     code = pioctl(0, VIOCCKSERV, &blob, 1);
1599     if (code) {
1600         if ((errno == EACCES) && (checkserv.tinterval > 0)) {
1601             printf("Must be root to change -interval\n");
1602             return code;
1603         }
1604         Die(errno, 0);
1605         return code;
1606     }
1607     memcpy(&temp, space, sizeof(afs_int32));
1608     if (checkserv.tinterval >= 0) {
1609         if (checkserv.tinterval > 0) 
1610             printf("The new down server probe interval (%d secs) is now in effect (old interval was %d secs)\n", 
1611                    checkserv.tinterval, temp);
1612         else 
1613             printf("The current down server probe interval is %d secs\n", temp);
1614         return 0;
1615     }
1616     if (temp == 0) {
1617         printf("All servers are running.\n");
1618     }
1619     else {
1620         printf("These servers unavailable due to network or server problems: ");
1621         for(j=0; j < MAXHOSTS; j++) {
1622             memcpy(&temp, space + j*sizeof(afs_int32), sizeof(afs_int32));
1623             if (temp == 0) break;
1624             tp = hostutil_GetNameByINet(temp);
1625             printf(" %s", tp);
1626         }
1627         printf(".\n");
1628         code = 1;       /* XXX */
1629     }
1630     return code;
1631 }
1632
1633 static GagCmd(as)
1634 register struct cmd_syndesc *as; {
1635     register afs_int32 code=0;
1636     struct ViceIoctl blob;
1637     struct gaginfo gagflags;
1638     struct cmd_item *show;
1639     
1640     memset(&gagflags, 0, sizeof(struct gaginfo));
1641     blob.in_size = sizeof(struct gaginfo);
1642     blob.in = (caddr_t ) &gagflags;
1643     blob.out_size = MAXSIZE;
1644     blob.out = space;
1645     memset(space, 0, sizeof(afs_int32));        /* so we assure zero when nothing is copied back */
1646
1647     if (show = as->parms[0].items)
1648        if (!foldcmp (show->data, "user"))
1649           gagflags.showflags |= GAGUSER;
1650        else if (!foldcmp (show->data, "console"))
1651           gagflags.showflags |= GAGCONSOLE;
1652        else if (!foldcmp (show->data, "all"))
1653           gagflags.showflags |= GAGCONSOLE | GAGUSER;
1654        else if (!foldcmp (show->data, "none"))
1655           /* do nothing */ ;
1656        else {
1657          fprintf(stderr, 
1658           "unrecognized flag %s: must be in {user,console,all,none}\n", 
1659           show->data);
1660          code = EINVAL;
1661        }
1662  
1663     if (code)
1664       return code;
1665
1666     code = pioctl(0, VIOC_GAG, &blob, 1);
1667     if (code) {
1668         Die(errno, 0);
1669         return code;
1670     }
1671     return code;
1672 }
1673
1674 static CheckVolumesCmd(as)
1675 register struct cmd_syndesc *as; {
1676     register afs_int32 code;
1677     struct ViceIoctl blob;
1678     
1679     blob.in_size = 0;
1680     blob.out_size = 0;
1681     code = pioctl(0, VIOCCKBACK, &blob, 1);
1682     if (code) {
1683         Die(errno, 0);
1684         return 1;
1685     }
1686     else printf("All volumeID/name mappings checked.\n");
1687     
1688     return 0;
1689 }
1690
1691 static SetCacheSizeCmd(as)
1692 register struct cmd_syndesc *as; {
1693     register afs_int32 code;
1694     struct ViceIoctl blob;
1695     afs_int32 temp;
1696     
1697     if (!as->parms[0].items && !as->parms[1].items) {
1698         fprintf(stderr,"%s: syntax error in set cache size cmd.\n", pn);
1699         return 1;
1700     }
1701     if (as->parms[0].items) {
1702         code = util_GetInt32(as->parms[0].items->data, &temp);
1703         if (code) {
1704             fprintf(stderr,"fs: bad integer specified for cache size.\n");
1705             return code;
1706         }
1707     }
1708     else
1709         temp = 0;
1710     blob.in = (char *) &temp;
1711     blob.in_size = sizeof(afs_int32);
1712     blob.out_size = 0;
1713     code = pioctl(0, VIOCSETCACHESIZE, &blob, 1);
1714     if (code)
1715         Die(errno, (char *) 0);
1716     else
1717         printf("New cache size set.\n");
1718     return 0;
1719 }
1720
1721 #define MAXGCSIZE       16
1722 static GetCacheParmsCmd(as)
1723 register struct cmd_syndesc *as; {
1724     register afs_int32 code;
1725     struct ViceIoctl blob;
1726     afs_int32 parms[MAXGCSIZE];
1727     
1728     blob.in = (char *) 0;
1729     blob.in_size = 0;
1730     blob.out_size = sizeof(parms);
1731     blob.out = (char *) parms;
1732     code = pioctl(0, VIOCGETCACHEPARMS, &blob, 1);
1733     if (code)
1734         Die(errno, (char *) 0);
1735     else {
1736         printf("AFS using %d of the cache's available %d 1K byte blocks.\n",
1737                parms[1], parms[0]);
1738         if (parms[1] > parms[0])
1739             printf("[Cache guideline temporarily deliberately exceeded; it will be adjusted down but you may wish to increase the cache size.]\n");
1740     }
1741     return 0;
1742 }
1743
1744 static ListCellsCmd(as)
1745 register struct cmd_syndesc *as; {
1746     register afs_int32 code;
1747     afs_int32 i, j, *lp, magic, size;
1748     char *tcp, *tp;
1749     afs_int32 clear, maxa = OMAXHOSTS;
1750     struct ViceIoctl blob;
1751     
1752     for(i=0;i<1000;i++) {
1753         tp = space;
1754         memcpy(tp, &i, sizeof(afs_int32));
1755         tp = (char *)(space + sizeof(afs_int32));
1756         lp = (afs_int32 *)tp;
1757         *lp++ = 0x12345678;
1758         size = sizeof(afs_int32) + sizeof(afs_int32);
1759         blob.out_size = MAXSIZE;
1760         blob.in_size = sizeof(afs_int32);
1761         blob.in = space;
1762         blob.out = space;
1763         code = pioctl(0, VIOCGETCELL, &blob, 1);
1764         if (code < 0) {
1765             if (errno == EDOM) break;   /* done with the list */
1766             else {
1767                 Die(errno, 0);
1768                 return 1;
1769             }
1770         }
1771         tp = space;
1772         memcpy(&magic, tp, sizeof(afs_int32));  
1773         if (magic == 0x12345678) {
1774             maxa = MAXHOSTS;
1775             tp += sizeof(afs_int32);
1776         }
1777         printf("Cell %s on hosts", tp+maxa*sizeof(afs_int32));
1778         for(j=0; j < maxa; j++) {
1779             memcpy(&clear, tp + j*sizeof(afs_int32), sizeof(afs_int32));
1780             if (clear == 0) break;
1781             tcp = hostutil_GetNameByINet(clear);
1782             printf(" %s", tcp);
1783         }
1784         printf(".\n");
1785     }
1786     return 0;
1787 }
1788
1789 static NewCellCmd(as)
1790 register struct cmd_syndesc *as; {
1791 #ifndef WIN32
1792     register afs_int32 code, linkedstate=0, size=0, *lp;
1793     struct ViceIoctl blob;
1794     register struct cmd_item *ti;
1795     register char *tp, *cellname=0;
1796     register struct hostent *thp;
1797     afs_int32 fsport = 0, vlport = 0;
1798
1799     memset(space, 0, MAXHOSTS * sizeof(afs_int32));
1800     tp = space;
1801     lp = (afs_int32 *)tp;
1802     *lp++ = 0x12345678;
1803     tp += sizeof(afs_int32);
1804     for(ti=as->parms[1].items; ti; ti=ti->next) {
1805         thp = hostutil_GetHostByName(ti->data);
1806         if (!thp) {
1807             fprintf(stderr,"%s: Host %s not found in host table, skipping it.\n",
1808                    pn, ti->data);
1809         }
1810         else {
1811             memcpy(tp, thp->h_addr, sizeof(afs_int32));
1812             tp += sizeof(afs_int32);
1813         }
1814     }
1815     if (as->parms[2].items) {
1816         /*
1817          * Link the cell, for the purposes of volume location, to the specified
1818          * cell.
1819          */
1820         cellname = as->parms[2].items->data;
1821         linkedstate = 1;
1822     }
1823 #ifdef FS_ENABLE_SERVER_DEBUG_PORTS
1824     if (as->parms[3].items) {
1825         code = util_GetInt32(as->parms[3].items->data, &vlport);
1826         if (code) {
1827             fprintf(stderr,"fs: bad integer specified for the fileserver port.\n");
1828             return code;
1829         }
1830     }
1831     if (as->parms[4].items) {
1832         code = util_GetInt32(as->parms[4].items->data, &fsport);
1833         if (code) {
1834             fprintf(stderr,"fs: bad integer specified for the vldb server port.\n");
1835             return code;
1836         }
1837     }
1838 #endif
1839     tp = (char *)(space + (MAXHOSTS+1) *sizeof(afs_int32));
1840     lp = (afs_int32 *)tp;    
1841     *lp++ = fsport;
1842     *lp++ = vlport;
1843     *lp = linkedstate;
1844     strcpy(space +  ((MAXHOSTS+4) * sizeof(afs_int32)), as->parms[0].items->data);
1845     size = ((MAXHOSTS+4) * sizeof(afs_int32)) + strlen(as->parms[0].items->data) + 1 /* for null */;
1846     tp = (char *)(space + size);
1847     if (linkedstate) {
1848         strcpy(tp, cellname);
1849         size += strlen(cellname) + 1;
1850     }
1851     blob.in_size = size;
1852     blob.in = space;
1853     blob.out_size = 0;
1854     code = pioctl(0, VIOCNEWCELL, &blob, 1);
1855     if (code < 0)
1856         Die(errno, 0);
1857     return 0;
1858 #else /* WIN32 */
1859     register afs_int32 code;
1860     struct ViceIoctl blob;
1861     
1862     blob.in_size = 0;
1863     blob.in = (char *) 0;
1864     blob.out_size = MAXSIZE;
1865     blob.out = space;
1866
1867     code = pioctl((char *) 0, VIOCNEWCELL, &blob, 1);
1868
1869     if (code) {
1870        Die(errno, (char *) 0);
1871     }
1872     else
1873        printf("Cell servers information refreshed\n");
1874     return 0;
1875 #endif /* WIN32 */
1876 }
1877
1878 static WhichCellCmd(as)
1879 register struct cmd_syndesc *as; {
1880     register afs_int32 code;
1881     struct ViceIoctl blob;
1882     register struct cmd_item *ti;
1883     int error;
1884     
1885     error = 0;  /* no error occurred */
1886     SetDotDefault(&as->parms[0].items);
1887     for(ti=as->parms[0].items; ti; ti=ti->next) {
1888         /* once per file */
1889         blob.in_size = 0;
1890         blob.out_size = MAXSIZE;
1891         blob.out = space;
1892
1893         code = pioctl(ti->data, VIOC_FILE_CELL_NAME, &blob, 1);
1894         if (code) {
1895             if (errno == ENOENT)
1896                 fprintf(stderr,"%s: no such cell as '%s'\n", pn, ti->data);
1897             else
1898                 Die(errno, ti->data);
1899             error = 1;
1900             continue;
1901         }
1902         else
1903             printf("File %s lives in cell '%s'\n", ti->data, space);
1904     }
1905     return error;
1906 }
1907
1908 static WSCellCmd(as)
1909 register struct cmd_syndesc *as; {
1910     register afs_int32 code;
1911     struct ViceIoctl blob;
1912     
1913     blob.in_size = 0;
1914     blob.in = (char *) 0;
1915     blob.out_size = MAXSIZE;
1916     blob.out = space;
1917
1918     code = pioctl((char *) 0, VIOC_GET_WS_CELL, &blob, 1);
1919
1920     if (code) {
1921         Die(errno, (char *) 0);
1922     }
1923     else
1924         printf("This workstation belongs to cell '%s'\n", space);
1925     return 0;
1926 }
1927
1928 static PrimaryCellCmd(as)
1929 register struct cmd_syndesc *as; {
1930 /*
1931     fprintf(stderr,"This command is obsolete, as is the concept of a primary token.\n");
1932 */
1933     return 0;
1934 }
1935
1936 static MonitorCmd(as)
1937 register struct cmd_syndesc *as; {
1938     register afs_int32 code;
1939     struct ViceIoctl blob;
1940     register struct cmd_item *ti;
1941     afs_int32 hostAddr;
1942     register struct hostent *thp;
1943     char *tp;
1944     int setp;
1945     
1946     ti = as->parms[0].items;
1947     setp = 1;
1948     if (ti) {
1949         /* set the host */
1950         if (!strcmp(ti->data, "off"))
1951             hostAddr = 0xffffffff;
1952         else {
1953             thp = hostutil_GetHostByName(ti->data);
1954             if (!thp) {
1955                 if (!strcmp(ti->data, "localhost")) {
1956                     fprintf(stderr,"localhost not in host table, assuming 127.0.0.1\n");
1957                     hostAddr = htonl(0x7f000001);
1958                 }
1959                 else {
1960                     fprintf(stderr,"host %s not found in host table.\n", ti->data);
1961                     return 1;
1962                 }
1963             }
1964             else memcpy(&hostAddr, thp->h_addr, sizeof(afs_int32));
1965         }
1966     }
1967     else {
1968         hostAddr = 0;   /* means don't set host */
1969         setp = 0;       /* aren't setting host */
1970     }
1971
1972     /* now do operation */
1973     blob.in_size = sizeof(afs_int32);
1974     blob.out_size = sizeof(afs_int32);
1975     blob.in = (char *) &hostAddr;
1976     blob.out = (char *) &hostAddr;
1977     code = pioctl(0, VIOC_AFS_MARINER_HOST, &blob, 1);
1978     if (code) {
1979         Die(errno, 0);
1980         exit(1);
1981     }
1982     if (setp) {
1983         printf("%s: new monitor host set.\n", pn);
1984     }
1985     else {
1986         /* now decode old address */
1987         if (hostAddr == 0xffffffff) {
1988             printf("Cache monitoring is currently disabled.\n");
1989         }
1990         else {
1991             tp = hostutil_GetNameByINet(hostAddr);
1992             printf("Using host %s for monitor services.\n", tp);
1993         }
1994     }
1995     return 0;
1996 }
1997
1998 static SysNameCmd(as)
1999 register struct cmd_syndesc *as; {
2000     register afs_int32 code;
2001     struct ViceIoctl blob;
2002     register struct cmd_item *ti;
2003     char *input = space;
2004     afs_int32 setp = 1;
2005     
2006     ti = as->parms[0].items;
2007     if (!ti) setp = 0;
2008     blob.in = space;
2009     blob.out = space;
2010     blob.out_size = MAXSIZE;
2011     blob.in_size = sizeof(afs_int32);
2012     memcpy(input, &setp, sizeof(afs_int32));
2013     input += sizeof(afs_int32);
2014     if (ti) {
2015         strcpy(input, ti->data);
2016         blob.in_size += strlen(ti->data) + 1;
2017         input += strlen(ti->data);
2018         *(input++) = '\0';
2019     }
2020     code = pioctl(0, VIOC_AFS_SYSNAME, &blob, 1);
2021     if (code) {
2022         Die(errno, 0);
2023         exit(1);
2024     }    
2025     if (setp) {
2026         printf("%s: new sysname set.\n", pn);
2027     }
2028     else {
2029         input = space;
2030         memcpy(&setp, input, sizeof(afs_int32));
2031         input += sizeof(afs_int32);
2032         if (!setp) {
2033             fprintf(stderr,"No sysname name value was found\n");
2034         } else {
2035             printf("Current sysname is '%s'\n", input);
2036         }
2037     }
2038     return 0;
2039 }
2040
2041 char *exported_types[] = {"null", "nfs", ""};
2042 static ExportAfsCmd(as)
2043 register struct cmd_syndesc *as; {
2044     register afs_int32 code;
2045     struct ViceIoctl blob;
2046     register struct cmd_item *ti;
2047     int export=0, type=0, mode = 0, exp = 0, gstat = 0, exportcall, pwsync=0, smounts=0;
2048     
2049     ti = as->parms[0].items;
2050     if (strcmp(ti->data, "nfs") == 0) type = 0x71; /* NFS */
2051     else {
2052         fprintf(stderr,"Invalid exporter type, '%s', Only the 'nfs' exporter is currently supported\n", ti->data);
2053         return 1;
2054     }
2055     ti = as->parms[1].items;
2056     if (ti) {
2057         if (strcmp(ti->data, "on") == 0) export = 3;
2058         else if (strcmp(ti->data, "off") == 0) export = 2;
2059         else {
2060             printf("Illegal argument %s\n", ti->data);
2061             return 1;
2062         }
2063         exp = 1;
2064     }
2065     if (ti = as->parms[2].items) {      /* -noconvert */
2066         if (strcmp(ti->data, "on") == 0) mode = 2;
2067         else if (strcmp(ti->data, "off") == 0) mode = 3;
2068         else {
2069             printf("Illegal argument %s\n", ti->data);
2070             return 1;
2071         }
2072     }
2073     if (ti = as->parms[3].items) {      /* -uidcheck */
2074         if (strcmp(ti->data, "on") == 0) pwsync = 3;
2075         else if (strcmp(ti->data, "off") == 0) pwsync = 2;
2076         else {
2077             printf("Illegal argument %s\n", ti->data);
2078             return 1;
2079         }
2080     }
2081     if (ti = as->parms[4].items) {      /* -submounts */
2082         if (strcmp(ti->data, "on") == 0) smounts = 3;
2083         else if (strcmp(ti->data, "off") == 0) smounts = 2;
2084         else {
2085             printf("Illegal argument %s\n", ti->data);
2086             return 1;
2087         }
2088     }
2089     exportcall =  (type << 24) | (mode << 6) | (pwsync << 4) | (smounts << 2) | export;
2090     type &= ~0x70;
2091     /* make the call */
2092     blob.in = (char *) &exportcall;
2093     blob.in_size = sizeof(afs_int32);
2094     blob.out = (char *) &exportcall;
2095     blob.out_size = sizeof(afs_int32);
2096     code = pioctl(0, VIOC_EXPORTAFS, &blob, 1);
2097     if (code) {
2098         if (errno == ENODEV) {
2099             fprintf(stderr,"Sorry, the %s-exporter type is currently not supported on this AFS client\n", exported_types[type]);
2100         } else {
2101             Die(errno, 0);
2102             exit(1);
2103         }
2104     } else {
2105         if (!gstat) {
2106             if (exportcall & 1) {
2107                 printf("'%s' translator is enabled with the following options:\n\tRunning in %s mode\n\tRunning in %s mode\n\t%s\n", 
2108                        exported_types[type], (exportcall & 2 ? "strict unix" : "convert owner mode bits to world/other"),
2109                        (exportcall & 4 ? "strict 'passwd sync'" : "no 'passwd sync'"),
2110                        (exportcall & 8 ? "Allow mounts of /afs/.. subdirs" : "Only mounts to /afs allowed"));
2111             } else {
2112                 printf("'%s' translator is disabled\n", exported_types[type]);
2113             }
2114         }
2115     }
2116     return(0);
2117 }
2118
2119
2120 static GetCellCmd(as)
2121 register struct cmd_syndesc *as; {
2122     register afs_int32 code;
2123     struct ViceIoctl blob;
2124     struct afsconf_cell info;
2125     register struct cmd_item *ti;
2126     struct a {
2127         afs_int32 stat;
2128         afs_int32 junk;
2129     } args;
2130     
2131     for(ti=as->parms[0].items; ti; ti=ti->next) {
2132         /* once per cell */
2133         blob.out_size = sizeof(args);
2134         blob.out = (caddr_t) &args;
2135         code = GetCellName(ti->data, &info);
2136         if (code) {
2137             continue;
2138         }
2139         blob.in_size = 1+strlen(info.name);
2140         blob.in = info.name;
2141         code = pioctl(0, VIOC_GETCELLSTATUS, &blob, 1);
2142         if (code) {
2143             if (errno == ENOENT)
2144                 fprintf(stderr,"fs: the cell named '%s' does not exist\n", info.name);
2145             else
2146                 Die(errno, info.name);
2147             return 1;
2148         }
2149         printf("Cell %s status: ", info.name);
2150 #ifdef notdef
2151         if (args.stat & 1) printf("primary ");
2152 #endif
2153         if (args.stat & 2) printf("no setuid allowed");
2154         else printf("setuid allowed");
2155         if (args.stat & 4) printf(", using old VLDB");
2156         printf("\n");
2157     }
2158     return 0;
2159 }
2160
2161 static SetCellCmd(as)
2162 register struct cmd_syndesc *as; {
2163     register afs_int32 code;
2164     struct ViceIoctl blob;
2165     struct afsconf_cell info;
2166     register struct cmd_item *ti;
2167     struct a {
2168         afs_int32 stat;
2169         afs_int32 junk;
2170         char cname[64];
2171     } args;
2172
2173     /* figure stuff to set */
2174     args.stat = 0;
2175     args.junk = 0;
2176
2177     if (! as->parms[1].items) args.stat |= CM_SETCELLFLAG_SUID; /* default to -nosuid */
2178
2179     /* set stat for all listed cells */
2180     for(ti=as->parms[0].items; ti; ti=ti->next) {
2181         /* once per cell */
2182         code = GetCellName(ti->data, &info);
2183         if (code) {
2184             continue;
2185         }
2186         strcpy(args.cname, info.name);
2187         blob.in_size = sizeof(args);
2188         blob.in = (caddr_t) &args;
2189         blob.out_size = 0;
2190         blob.out = (caddr_t) 0;
2191         code = pioctl(0, VIOC_SETCELLSTATUS, &blob, 1);
2192         if (code) {
2193             Die(errno, 0);
2194             exit(1);
2195         }
2196     }
2197     return 0;
2198 }
2199
2200 #ifdef WIN32
2201 static GetCellName(char *cellNamep, struct afsconf_cell *infop)
2202 {
2203         strcpy(infop->name, cellNamep);
2204         return 0;
2205 }
2206
2207 static VLDBInit(int noAuthFlag, struct afsconf_cell *infop)
2208 {
2209         return 0;
2210 }
2211
2212 #else /* not WIN32 */
2213
2214 static GetCellName(cellName, info)
2215 char *cellName;
2216 struct afsconf_cell *info;
2217 {
2218     struct afsconf_dir *tdir;
2219     register int code;
2220
2221     tdir = afsconf_Open(AFSCONF_CLIENTNAME);
2222     if (!tdir) {
2223         fprintf(stderr,"Could not process files in configuration directory (%s).\n",AFSCONF_CLIENTNAME);
2224         return -1;
2225     }
2226
2227     code = afsconf_GetCellInfo(tdir, cellName, AFSCONF_VLDBSERVICE, info);
2228     if (code) {
2229         fprintf(stderr,"fs: cell %s not in %s/CellServDB\n", cellName, AFSCONF_CLIENTNAME);
2230         return code;
2231     }
2232
2233     return 0;
2234 }
2235
2236
2237 static VLDBInit(noAuthFlag,  info)
2238 int noAuthFlag;
2239 struct afsconf_cell *info;
2240 {   afs_int32 code;
2241     struct ktc_principal sname;
2242     struct ktc_token ttoken;
2243     afs_int32 scIndex;
2244     struct rx_securityClass *sc;
2245     struct rx_connection *serverconns[VLDB_MAXSERVERS];
2246     afs_int32 i;
2247
2248     code = rx_Init(0);
2249     if (code) {
2250         fprintf(stderr,"fs: could not initialize rx.\n");
2251         return code;
2252     }
2253     rxInitDone = 1;
2254     rx_SetRxDeadTime(50);
2255     if (!noAuthFlag) {   /* we don't need tickets for null */
2256         strcpy(sname.cell, info->name);
2257         sname.instance[0] = 0;
2258         strcpy(sname.name, "afs");
2259         code = ktc_GetToken(&sname,&ttoken, sizeof(ttoken), (char *)0);
2260         if (code) {
2261             fprintf(stderr,"fs: Could not get afs tokens, running unauthenticated.\n");
2262             scIndex = 0;
2263         }
2264         else {
2265             /* got a ticket */
2266             if (ttoken.kvno >= 0 && ttoken.kvno <= 255) scIndex = 2;    /* kerberos */
2267             else {
2268                 fprintf (stderr, "fs: funny kvno (%d) in ticket, proceeding\n",
2269                          ttoken.kvno);
2270                 scIndex = 2;
2271             }
2272         }
2273     }
2274     else scIndex = 0;       /* don't authenticate */
2275     switch (scIndex) {
2276         case 0 :
2277             sc = (struct rx_securityClass *) rxnull_NewClientSecurityObject();
2278             break;
2279         
2280         case 1 :
2281             break;
2282         case 2:
2283             sc = (struct rx_securityClass *)
2284                 rxkad_NewClientSecurityObject(rxkad_clear, &ttoken.sessionKey,
2285                                               ttoken.kvno, ttoken.ticketLen,
2286                                               ttoken.ticket);
2287             break;
2288     }
2289     if (info->numServers > VLDB_MAXSERVERS) {
2290         fprintf(stderr, "fs: info.numServers=%d (> VLDB_MAXSERVERS=%d)\n",
2291                 info->numServers, VLDB_MAXSERVERS);
2292         exit(1);
2293     }
2294     memset(serverconns, 0, sizeof(serverconns));
2295     for (i = 0;i<info->numServers;i++) 
2296         serverconns[i] = rx_NewConnection(info->hostAddr[i].sin_addr.s_addr,
2297                                           info->hostAddr[i].sin_port, USER_SERVICE_ID,
2298                                           sc, scIndex);
2299
2300     code = ubik_ClientInit(serverconns, &uclient);
2301
2302     if (code) {
2303         fprintf(stderr,"fs: ubik client init failed.\n");
2304         return code;
2305     }
2306     return 0;
2307 }
2308 #endif /* not WIN32 */
2309
2310 static    struct ViceIoctl gblob;
2311 static int debug = 0;
2312 /* 
2313  * here follow some routines in suport of the setserverprefs and
2314  * getserverprefs commands.  They are:
2315  * SetPrefCmd  "top-level" routine
2316  * addServer   adds a server to the list of servers to be poked into the
2317  *             kernel.  Will poke the list into the kernel if it threatens
2318  *             to get too large.
2319  * pokeServers pokes the existing list of servers and ranks into the kernel
2320  * GetPrefCmd  reads the Cache Manager's current list of server ranks
2321  */
2322
2323 static pokeServers()
2324 {
2325 int code;
2326
2327     cm_SSetPref_t *ssp;
2328     code = pioctl(0, VIOC_SETSPREFS, &gblob, 1);
2329
2330     ssp = (cm_SSetPref_t *)space;
2331     gblob.in_size = ((char *)&(ssp->servers[0])) - (char *)ssp;
2332     gblob.in = space;
2333     return code;
2334 }
2335
2336 static addServer(name, rank)
2337         char *name;
2338         unsigned short rank;
2339 {  
2340         int code;
2341         cm_SSetPref_t *ssp;
2342         cm_SPref_t *sp;
2343         struct hostent *thostent;
2344
2345 #ifndef MAXUSHORT
2346 #ifdef MAXSHORT
2347 #define MAXUSHORT ((unsigned short) 2*MAXSHORT+1)  /* assumes two's complement binary system */
2348 #else
2349 #define MAXUSHORT ((unsigned short) ~0)
2350 #endif
2351 #endif
2352
2353         code = 0;
2354         thostent = hostutil_GetHostByName(name);
2355         if (!thostent) {
2356                 fprintf (stderr, "%s: couldn't resolve name.\n", name);
2357                 return EINVAL;
2358         }
2359
2360         ssp = (cm_SSetPref_t *)(gblob.in);
2361
2362         if (gblob.in_size > MAXINSIZE - sizeof(cm_SPref_t)) {
2363                 code = pokeServers();
2364                 ssp->num_servers = 0;
2365         }
2366
2367         sp = (cm_SPref_t *)((char*)gblob.in + gblob.in_size);
2368         memcpy (&(sp->host.s_addr), thostent->h_addr, sizeof(afs_uint32));
2369         sp->rank = (rank > MAXUSHORT ? MAXUSHORT : rank);
2370         gblob.in_size += sizeof(cm_SPref_t);
2371         ssp->num_servers++;
2372
2373         if (debug) fprintf(stderr, "adding server %s, rank %d, ip addr 0x%lx\n",name,sp->rank,sp->host.s_addr);
2374         
2375         return code;
2376 }
2377
2378
2379 static SetPrefCmd(as)
2380 register struct cmd_syndesc *as; {
2381   FILE *infd;
2382   afs_int32 code;
2383   struct cmd_item *ti;
2384   char name[80];
2385   afs_int32 rank;
2386   cm_SSetPref_t *ssp;
2387     
2388   ssp = (cm_SSetPref_t *)space;
2389   ssp->flags = 0;
2390   ssp->num_servers = 0;
2391   gblob.in_size = ((char*)&(ssp->servers[0])) - (char *)ssp;
2392   gblob.in = space;
2393   gblob.out = space;
2394   gblob.out_size = MAXSIZE;
2395
2396
2397 #ifndef WIN32
2398   if (geteuid()) {
2399     fprintf (stderr,"Permission denied: requires root access.\n");
2400     return EACCES;
2401   }
2402 #endif /* WIN32 */
2403
2404   code = 0;
2405
2406   ti = as->parms[2].items;  /* -file */
2407   if (ti) {
2408     if (debug) fprintf(stderr,"opening file %s\n",ti->data);
2409     if (!(infd = fopen(ti->data,"r" ))) {
2410       code = errno;
2411       Die(errno,ti->data);
2412     }
2413     else
2414       while ( fscanf(infd, "%79s%ld", name, &rank) != EOF) {
2415         code = addServer (name, (unsigned short) rank);
2416       }
2417   }
2418
2419   ti = as->parms[3].items;  /* -stdin */
2420   if (ti) {
2421     while ( scanf("%79s%ld", name, &rank) != EOF) {
2422       code = addServer (name, (unsigned short) rank);
2423     }
2424   }
2425   
2426   for (ti = as->parms[0].items;ti;ti=ti->next) {/*list of servers, ranks */
2427     if (ti) {
2428       if (!ti->next) {
2429         break;
2430       }
2431       code = addServer (ti->data, (unsigned short) atol(ti->next->data));
2432       if (debug)
2433         printf("set fs prefs %s %s\n", ti->data, ti->next->data);
2434       ti=ti->next;
2435     }
2436   }
2437   code = pokeServers();
2438   if (debug) 
2439     printf("now working on vlservers, code=%d, errno=%d\n",code,errno);
2440
2441   ssp = (cm_SSetPref_t *)space;
2442   gblob.in_size = ((char*)&(ssp->servers[0])) - (char *)ssp;
2443   gblob.in = space;
2444   ssp->flags = CM_SPREF_VLONLY;
2445   ssp->num_servers = 0;
2446
2447   for (ti = as->parms[1].items;ti;ti=ti->next) { /* list of dbservers, ranks */
2448     if (ti) {
2449       if (!ti->next) {
2450         break;
2451       }
2452       code = addServer (ti->data, (unsigned short) atol(ti->next->data));
2453       if (debug) 
2454         printf("set vl prefs %s %s\n", ti->data, ti->next->data);
2455       ti=ti->next;
2456     }
2457   }
2458
2459   if (as->parms[1].items) {
2460     if (debug) 
2461       printf("now poking vlservers\n");
2462     code = pokeServers();
2463   }
2464
2465 if (code) 
2466   Die(errno,0);
2467
2468 return code;
2469 }
2470
2471
2472 static GetPrefCmd(as)
2473 register struct cmd_syndesc *as; {
2474   afs_int32 code;
2475   struct cmd_item *ti;
2476   char *name, tbuffer[20];
2477   afs_int32 addr;
2478   FILE * outfd;
2479   int resolve;
2480   int vlservers;
2481   struct ViceIoctl blob;
2482   struct cm_SPrefRequest *in;
2483   struct cm_SPrefInfo *out;
2484   int i;
2485     
2486   code = 0;
2487   ti = as->parms[0].items;  /* -file */
2488   if (ti) {
2489     if (debug) fprintf(stderr,"opening file %s\n",ti->data);
2490     if (!(outfd = freopen(ti->data,"w",stdout))) {
2491       Die(errno,ti->data);
2492       return errno;
2493     }
2494   }
2495
2496   ti = as->parms[1].items;  /* -numeric */
2497   resolve = !(ti);
2498   ti = as->parms[2].items;  /* -vlservers */
2499   vlservers = (ti ? CM_SPREF_VLONLY : 0);
2500 /*  ti = as->parms[3].items;   -cell */
2501
2502   in = (struct cm_SPrefRequest *)space;
2503   in->offset = 0;
2504
2505   do {
2506     blob.in_size=sizeof(struct cm_SPrefRequest);
2507     blob.in = (char *)in;
2508     blob.out = space;
2509     blob.out_size = MAXSIZE;
2510
2511     in->num_servers = (MAXSIZE - 2*sizeof(short))/sizeof(struct cm_SPref);
2512     in->flags = vlservers; 
2513
2514     code = pioctl(0, VIOC_GETSPREFS, &blob, 1);
2515     if (code){
2516       perror("getserverprefs pioctl");
2517       Die (errno,0);
2518     }
2519     else {
2520       out = (struct cm_SPrefInfo *) blob.out;
2521
2522       for (i=0;i<out->num_servers;i++) {
2523         if (resolve) {
2524           name = hostutil_GetNameByINet(out->servers[i].host.s_addr);
2525         }
2526         else {
2527           addr = ntohl(out->servers[i].host.s_addr);
2528           sprintf(tbuffer, "%d.%d.%d.%d", (addr>>24) & 0xff, (addr>>16) & 0xff,
2529                   (addr>>8) & 0xff, addr & 0xff);
2530           name=tbuffer;
2531         }
2532         printf ("%-50s %5u\n",name,out->servers[i].rank);      
2533       }
2534   
2535       in->offset = out->next_offset;
2536     }
2537   } while (!code && out->next_offset > 0);
2538
2539 return code;
2540 }
2541
2542 static TraceCmd(struct cmd_syndesc *asp)
2543 {
2544         long code;
2545         struct ViceIoctl blob;
2546         long inValue;
2547         long outValue;
2548         
2549         if ((asp->parms[0].items && asp->parms[1].items)) {
2550                 fprintf(stderr, "fs trace: must use at most one of '-off' or '-on'\n");
2551                 return EINVAL;
2552         }
2553         
2554         /* determine if we're turning this tracing on or off */
2555         inValue = 0;
2556         if (asp->parms[0].items)
2557                 inValue = 3;            /* enable */
2558         else if (asp->parms[1].items) inValue = 2;      /* disable */
2559         if (asp->parms[2].items) inValue |= 4;          /* do reset */
2560         if (asp->parms[3].items) inValue |= 8;          /* dump */
2561         
2562         blob.in_size = sizeof(long);
2563         blob.in = (char *) &inValue;
2564         blob.out_size = sizeof(long);
2565         blob.out = (char *) &outValue;
2566         
2567         code = pioctl(NULL, VIOC_TRACECTL, &blob, 1);
2568         if (code) {
2569                 Die(errno, NULL);
2570                 return code;
2571         }
2572         
2573         if (outValue) printf("AFS tracing enabled.\n");
2574         else printf("AFS tracing disabled.\n");
2575
2576         return 0;
2577 }
2578
2579 static void sbusage()
2580 {
2581         fprintf(stderr, "example usage: fs storebehind -files *.o -kb 99999 -default 0\n");
2582         fprintf(stderr, "               fs sb 50000 *.[ao] -default 10\n");
2583 }
2584
2585 static StoreBehindCmd(as)  /* fs sb -kbytes 9999 -files *.o -default 64 */
2586 struct cmd_syndesc *as; {
2587     afs_int32 code;
2588     struct ViceIoctl blob;
2589     struct cmd_item *ti;
2590     struct sbstruct tsb;
2591     int kb;
2592     
2593     if ((as->parms[0].items && as->parms[1].items) ||   
2594         (!as->parms[0].items && !as->parms[1].items)) /* same as logical xor */
2595       ;
2596     else {
2597       sbusage();
2598       return EINVAL;
2599     }
2600
2601     ti=as->parms[2].items;
2602     if (ti && ti->data) {
2603       kb = atoi (ti->data);
2604     }
2605     else kb = -1;
2606     tsb.sb_default = kb;
2607
2608     ti=as->parms[1].items;
2609     if (ti && ti->data) {
2610       kb = atoi (ti->data);
2611     }
2612     else kb = -1;
2613     tsb.sb_thisfile = kb;
2614     
2615     ti=as->parms[0].items;
2616     do { 
2617       /* once per file */
2618       blob.in = &tsb;
2619       blob.out = &tsb;
2620       blob.in_size = sizeof(tsb);
2621       blob.out_size = sizeof(tsb);
2622       fprintf (stderr, "storbehind %s %d %d\n", (ti?ti->data:0), 
2623                tsb.sb_thisfile, tsb.sb_default);
2624       code = pioctl((ti ? ti->data : 0) , VIOC_STOREBEHIND, &blob, 1);
2625       if (code) {
2626         Die(errno, (ti ? ti->data : 0));
2627         continue;
2628       }
2629       if (blob.out_size == sizeof(tsb)) {
2630         fprintf (stderr, "storbehind %s is now %d (default %d)\n", (ti?ti->data:0), 
2631                  tsb.sb_thisfile, tsb.sb_default);
2632       }
2633       ti = (ti ? ti->next : ti);
2634     } while (ti);
2635
2636     return 0;
2637 }
2638
2639 static afs_int32 SetCryptCmd(as)
2640     struct cmd_syndesc *as;
2641 {
2642     afs_int32 code = 0, flag;
2643     struct ViceIoctl blob;
2644     char *tp;
2645  
2646     tp = as->parms[0].items->data;
2647     if (strcmp(tp, "on") == 0)
2648       flag = 1;
2649     else if (strcmp(tp, "off") == 0)
2650       flag = 0;
2651     else {
2652       fprintf (stderr, "%s: %s must be \"on\" or \"off\".\n", pn, tp);
2653       return EINVAL;
2654     }
2655
2656     blob.in = (char *) &flag;
2657     blob.in_size = sizeof(flag);
2658     blob.out_size = 0;
2659     code = pioctl(0, VIOC_SETRXKCRYPT, &blob, 1);
2660     if (code)
2661       Die(code, (char *) 0);
2662     return 0;
2663 }
2664
2665 static afs_int32 GetCryptCmd(as)
2666     struct cmd_syndesc *as;
2667 {
2668     afs_int32 code = 0, flag;
2669     struct ViceIoctl blob;
2670     char *tp;
2671  
2672     blob.in = (char *) 0;
2673     blob.in_size = 0;
2674     blob.out_size = sizeof(flag);
2675     blob.out = space;
2676
2677     code = pioctl(0, VIOC_GETRXKCRYPT, &blob, 1);
2678
2679     if (code) Die(code, (char *) 0);
2680     else {
2681       tp = space;
2682       memcpy(&flag, tp, sizeof(afs_int32));
2683       printf("Security level is currently ");
2684       if (flag == 1)
2685         printf("crypt (data security).\n");
2686       else
2687         printf("clear.\n");
2688     }
2689     return 0;
2690 }
2691
2692 main(argc, argv)
2693 int argc;
2694 char **argv; {
2695     register afs_int32 code;
2696     register struct cmd_syndesc *ts;
2697     
2698 #ifdef  AFS_AIX32_ENV
2699     /*
2700      * The following signal action for AIX is necessary so that in case of a 
2701      * crash (i.e. core is generated) we can include the user's data section 
2702      * in the core dump. Unfortunately, by default, only a partial core is
2703      * generated which, in many cases, isn't too useful.
2704      */
2705     struct sigaction nsa;
2706     
2707     sigemptyset(&nsa.sa_mask);
2708     nsa.sa_handler = SIG_DFL;
2709     nsa.sa_flags = SA_FULLDUMP;
2710     sigaction(SIGSEGV, &nsa, NULL);
2711 #endif
2712
2713 #ifdef WIN32
2714     WSADATA WSAjunk;
2715     WSAStartup(0x0101, &WSAjunk);
2716 #endif /* WIN32 */
2717
2718     /* try to find volume location information */
2719     
2720
2721     osi_Init();
2722
2723     ts = cmd_CreateSyntax("setserverprefs", SetPrefCmd, 0, "set server ranks");
2724     cmd_AddParm(ts, "-servers", CMD_LIST, CMD_OPTIONAL|CMD_EXPANDS, "fileserver names and ranks");
2725     cmd_AddParm(ts, "-vlservers", CMD_LIST, CMD_OPTIONAL|CMD_EXPANDS, "VL server names and ranks");
2726     cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_OPTIONAL, "input from named file");
2727     cmd_AddParm(ts, "-stdin", CMD_FLAG, CMD_OPTIONAL, "input from stdin");
2728     cmd_CreateAlias(ts, "sp");
2729
2730     ts = cmd_CreateSyntax("getserverprefs", GetPrefCmd, 0, "get server ranks");
2731     cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_OPTIONAL, "output to named file");
2732     cmd_AddParm(ts, "-numeric", CMD_FLAG, CMD_OPTIONAL, "addresses only");
2733     cmd_AddParm(ts, "-vlservers", CMD_FLAG, CMD_OPTIONAL, "VL servers");
2734     /* cmd_AddParm(ts, "-cell", CMD_FLAG, CMD_OPTIONAL, "cellname"); */
2735     cmd_CreateAlias(ts, "gp");
2736
2737     ts = cmd_CreateSyntax("setacl", SetACLCmd, 0, "set access control list");
2738     cmd_AddParm(ts, "-dir", CMD_LIST, 0, "directory");
2739     cmd_AddParm(ts, "-acl", CMD_LIST, 0, "access list entries");
2740     cmd_AddParm(ts, "-clear", CMD_FLAG, CMD_OPTIONAL, "clear access list");
2741     cmd_AddParm(ts, "-negative", CMD_FLAG, CMD_OPTIONAL, "apply to negative rights");
2742     parm_setacl_id = ts->nParms;
2743     cmd_AddParm(ts, "-id", CMD_FLAG, CMD_OPTIONAL, "initial directory acl (DFS only)");
2744     cmd_AddParm(ts, "-if", CMD_FLAG, CMD_OPTIONAL, "initial file acl (DFS only)");
2745     cmd_CreateAlias(ts, "sa");
2746     
2747     ts = cmd_CreateSyntax("listacl", ListACLCmd, 0, "list access control list");
2748     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
2749     parm_listacl_id = ts->nParms;
2750     cmd_AddParm(ts, "-id", CMD_FLAG, CMD_OPTIONAL, "initial directory acl");
2751     cmd_AddParm(ts, "-if", CMD_FLAG, CMD_OPTIONAL, "initial file acl");
2752     cmd_CreateAlias(ts, "la");
2753     
2754     ts = cmd_CreateSyntax("cleanacl", CleanACLCmd, 0, "clean up access control list");
2755     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
2756     
2757     ts = cmd_CreateSyntax("copyacl", CopyACLCmd, 0, "copy access control list");
2758     cmd_AddParm(ts, "-fromdir", CMD_SINGLE, 0, "source directory (or DFS file)");
2759     cmd_AddParm(ts, "-todir", CMD_LIST, 0, "destination directory (or DFS file)");
2760     cmd_AddParm(ts, "-clear", CMD_FLAG, CMD_OPTIONAL, "first clear dest access list");
2761     parm_copyacl_id = ts->nParms;
2762     cmd_AddParm(ts, "-id", CMD_FLAG, CMD_OPTIONAL, "initial directory acl");
2763     cmd_AddParm(ts, "-if", CMD_FLAG, CMD_OPTIONAL, "initial file acl");
2764     
2765     cmd_CreateAlias(ts, "ca");
2766
2767     ts = cmd_CreateSyntax("flush", FlushCmd, 0, "flush file from cache");
2768     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
2769     
2770     ts = cmd_CreateSyntax("setvol", SetVolCmd, 0, "set volume status");
2771     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
2772     cmd_AddParm(ts, "-max", CMD_SINGLE, CMD_OPTIONAL, "disk space quota in 1K units");
2773 #ifdef notdef
2774     cmd_AddParm(ts, "-min", CMD_SINGLE, CMD_OPTIONAL, "disk space guaranteed");
2775 #endif
2776     cmd_AddParm(ts, "-motd", CMD_SINGLE, CMD_OPTIONAL, "message of the day");
2777     cmd_AddParm(ts, "-offlinemsg", CMD_SINGLE, CMD_OPTIONAL, "offline message");
2778     cmd_CreateAlias(ts, "sv");
2779     
2780     ts = cmd_CreateSyntax("messages", GagCmd, 0, "control Cache Manager messages");
2781     cmd_AddParm(ts, "-show", CMD_SINGLE, CMD_OPTIONAL, "[user|console|all|none]");
2782
2783     ts = cmd_CreateSyntax("examine", ExamineCmd, 0, "display volume status");
2784     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
2785     cmd_CreateAlias(ts, "lv");
2786     cmd_CreateAlias(ts, "listvol");
2787     
2788     ts = cmd_CreateSyntax("listquota", ListQuotaCmd, 0, "list volume quota");
2789     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
2790     cmd_CreateAlias(ts, "lq");
2791     
2792     ts = cmd_CreateSyntax("diskfree", DiskFreeCmd, 0, "show server disk space usage");
2793     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
2794     cmd_CreateAlias(ts, "df");
2795     
2796     ts = cmd_CreateSyntax("quota", QuotaCmd, 0, "show volume quota usage");
2797     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
2798     
2799     ts = cmd_CreateSyntax("lsmount", ListMountCmd, 0, "list mount point");    
2800     cmd_AddParm(ts, "-dir", CMD_LIST, 0, "directory");
2801     
2802     ts = cmd_CreateSyntax("mkmount", MakeMountCmd, 0, "make mount point");
2803     cmd_AddParm(ts, "-dir", CMD_SINGLE, 0, "directory");
2804     cmd_AddParm(ts, "-vol", CMD_SINGLE, 0, "volume name");
2805     cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell name");
2806     cmd_AddParm(ts, "-rw", CMD_FLAG, CMD_OPTIONAL, "force r/w volume");
2807     cmd_AddParm(ts, "-fast", CMD_FLAG, CMD_OPTIONAL, "don't check name with VLDB");
2808
2809     /*
2810      *
2811      * defect 3069
2812      * 
2813     cmd_AddParm(ts, "-root", CMD_FLAG, CMD_OPTIONAL, "create cellular mount point");
2814     */
2815
2816     
2817     ts = cmd_CreateSyntax("rmmount", RemoveMountCmd, 0, "remove mount point");
2818     cmd_AddParm(ts, "-dir", CMD_LIST, 0, "directory");
2819     
2820     ts = cmd_CreateSyntax("checkservers", CheckServersCmd, 0, "check local cell's servers");
2821     cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell to check");
2822     cmd_AddParm(ts, "-all", CMD_FLAG, CMD_OPTIONAL, "check all cells");
2823     cmd_AddParm(ts, "-fast", CMD_FLAG, CMD_OPTIONAL, "just list, don't check");
2824         cmd_AddParm(ts,"-interval",CMD_SINGLE,CMD_OPTIONAL,"seconds between probes");
2825     
2826     ts = cmd_CreateSyntax("checkvolumes", CheckVolumesCmd,0, "check volumeID/name mappings");
2827     cmd_CreateAlias(ts, "checkbackups");
2828
2829     
2830     ts = cmd_CreateSyntax("setcachesize", SetCacheSizeCmd, 0, "set cache size");
2831     cmd_AddParm(ts, "-blocks", CMD_SINGLE, CMD_OPTIONAL, "size in 1K byte blocks (0 => reset)");
2832     cmd_CreateAlias(ts, "cachesize");
2833
2834     cmd_AddParm(ts, "-reset", CMD_FLAG, CMD_OPTIONAL, "reset size back to boot value");
2835     
2836     ts = cmd_CreateSyntax("getcacheparms", GetCacheParmsCmd, 0, "get cache usage info");
2837
2838     ts = cmd_CreateSyntax("listcells", ListCellsCmd, 0, "list configured cells");
2839     
2840     ts = cmd_CreateSyntax("setquota", SetQuotaCmd, 0, "set volume quota");
2841     cmd_AddParm(ts, "-path", CMD_SINGLE, CMD_OPTIONAL, "dir/file path");
2842     cmd_AddParm(ts, "-max", CMD_SINGLE, 0, "max quota in kbytes");
2843 #ifdef notdef
2844     cmd_AddParm(ts, "-min", CMD_SINGLE, CMD_OPTIONAL, "min quota in kbytes");
2845 #endif
2846     cmd_CreateAlias(ts, "sq");
2847
2848     ts = cmd_CreateSyntax("newcell", NewCellCmd, 0, "configure new cell");
2849 #ifndef WIN32
2850     cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "cell name");
2851     cmd_AddParm(ts, "-servers", CMD_LIST, CMD_REQUIRED, "primary servers");
2852     cmd_AddParm(ts, "-linkedcell", CMD_SINGLE, CMD_OPTIONAL, "linked cell name");
2853 #endif
2854
2855 #ifdef FS_ENABLE_SERVER_DEBUG_PORTS
2856     /*
2857      * Turn this on only if you wish to be able to talk to a server which is listening
2858      * on alternative ports. This is not intended for general use and may not be
2859      * supported in the cache manager. It is not a way to run two servers at the
2860      * same host, since the cache manager cannot properly distinguish those two hosts.
2861      */
2862     cmd_AddParm(ts, "-fsport", CMD_SINGLE, CMD_OPTIONAL, "cell's fileserver port");
2863     cmd_AddParm(ts, "-vlport", CMD_SINGLE, CMD_OPTIONAL, "cell's vldb server port");
2864 #endif
2865
2866     ts = cmd_CreateSyntax("whichcell", WhichCellCmd, 0, "list file's cell");
2867     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
2868
2869     ts = cmd_CreateSyntax("whereis", WhereIsCmd, 0, "list file's location");
2870     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
2871
2872     ts = cmd_CreateSyntax("wscell", WSCellCmd, 0, "list workstation's cell");
2873     
2874     /*
2875      ts = cmd_CreateSyntax("primarycell", PrimaryCellCmd, 0, "obsolete (listed primary cell)");
2876      */
2877     
2878     ts = cmd_CreateSyntax("monitor", MonitorCmd, 0, "set cache monitor host address");
2879     cmd_AddParm(ts, "-server", CMD_SINGLE, CMD_OPTIONAL, "host name or 'off'");
2880     cmd_CreateAlias(ts, "mariner");
2881     
2882    
2883     ts = cmd_CreateSyntax("getcellstatus", GetCellCmd, 0, "get cell status");
2884     cmd_AddParm(ts, "-cell", CMD_LIST, 0, "cell name");
2885     
2886     ts = cmd_CreateSyntax("setcell", SetCellCmd, 0, "set cell status");
2887     cmd_AddParm(ts, "-cell", CMD_LIST, 0, "cell name");
2888     cmd_AddParm(ts, "-suid", CMD_FLAG, CMD_OPTIONAL, "allow setuid programs");
2889     cmd_AddParm(ts, "-nosuid", CMD_FLAG, CMD_OPTIONAL, "disallow setuid programs");
2890     
2891     ts = cmd_CreateSyntax("flushvolume", FlushVolumeCmd, 0, "flush all data in volume");
2892     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
2893
2894     ts = cmd_CreateSyntax("sysname", SysNameCmd, 0, "get/set sysname (i.e. @sys) value");
2895     cmd_AddParm(ts, "-newsys", CMD_SINGLE, CMD_OPTIONAL, "new sysname");
2896
2897     ts = cmd_CreateSyntax("exportafs", ExportAfsCmd, 0, "enable/disable translators to AFS");
2898     cmd_AddParm(ts, "-type", CMD_SINGLE, 0, "exporter name");
2899     cmd_AddParm(ts, "-start", CMD_SINGLE, CMD_OPTIONAL, "start/stop translator ('on' or 'off')");
2900     cmd_AddParm(ts, "-convert", CMD_SINGLE, CMD_OPTIONAL, "convert from afs to unix mode ('on or 'off')");
2901     cmd_AddParm(ts, "-uidcheck", CMD_SINGLE, CMD_OPTIONAL, "run on strict 'uid check' mode ('on' or 'off')");
2902     cmd_AddParm(ts, "-submounts", CMD_SINGLE, CMD_OPTIONAL, "allow nfs mounts to subdirs of /afs/.. ('on' or 'off')");
2903
2904
2905     ts = cmd_CreateSyntax("storebehind", StoreBehindCmd, 0, 
2906                           "store to server after file close");
2907     cmd_AddParm(ts, "-kbytes", CMD_SINGLE, CMD_OPTIONAL, "asynchrony for specified names");
2908     cmd_AddParm(ts, "-files", CMD_LIST, CMD_OPTIONAL, "specific pathnames");
2909     cmd_AddParm(ts, "-allfiles", CMD_SINGLE, CMD_OPTIONAL, "new default (KB)");
2910     cmd_CreateAlias(ts, "sb");
2911
2912     ts = cmd_CreateSyntax("setcrypt", SetCryptCmd, 0, "set cache manager encryption flag");
2913     cmd_AddParm(ts, "-crypt", CMD_SINGLE, 0, "on or off");
2914
2915     ts = cmd_CreateSyntax("getcrypt", GetCryptCmd, 0, "get cache manager encryption flag");
2916
2917     ts = cmd_CreateSyntax("trace", TraceCmd, 0, "enable or disable CM tracing");
2918     cmd_AddParm(ts, "-on", CMD_FLAG, CMD_OPTIONAL, "enable tracing");
2919     cmd_AddParm(ts, "-off", CMD_FLAG, CMD_OPTIONAL, "disable tracing");
2920     cmd_AddParm(ts, "-reset", CMD_FLAG, CMD_OPTIONAL, "reset log contents");
2921     cmd_AddParm(ts, "-dump", CMD_FLAG, CMD_OPTIONAL, "dump log contents");
2922     cmd_CreateAlias(ts, "tr");
2923
2924     ts = cmd_CreateSyntax("memdump", MemDumpCmd, 0, "dump memory allocs in debug builds");
2925     cmd_AddParm(ts, "-begin", CMD_FLAG, CMD_OPTIONAL, "set a memory checkpoint");
2926     cmd_AddParm(ts, "-end", CMD_FLAG, CMD_OPTIONAL, "dump memory allocs");
2927     
2928     code = cmd_Dispatch(argc, argv);
2929
2930 #ifndef WIN32
2931     if (rxInitDone) rx_Finalize();
2932 #endif /* not WIN32 */
2933     
2934     return code;
2935 }
2936
2937 void Die(code, filename)
2938     int   code;
2939     char *filename;
2940 { /*Die*/
2941
2942     if (code == EINVAL) {
2943         if (filename)
2944             fprintf(stderr,"%s: Invalid argument; it is possible that %s is not in AFS.\n", pn, filename);
2945         else fprintf(stderr,"%s: Invalid argument.\n", pn);
2946     }
2947     else if (code == ENOENT) {
2948         if (filename) fprintf(stderr,"%s: File '%s' doesn't exist\n", pn, filename);
2949         else fprintf(stderr,"%s: no such file returned\n", pn);
2950     }
2951     else if (code == EROFS)  fprintf(stderr,"%s: You can not change a backup or readonly volume\n", pn);
2952     else if (code == EACCES || code == EPERM) {
2953         if (filename) fprintf(stderr,"%s: You don't have the required access rights on '%s'\n", pn, filename);
2954         else fprintf(stderr,"%s: You do not have the required rights to do this operation\n", pn);
2955     }
2956     else if (code == ENODEV) {
2957         fprintf(stderr,"%s: AFS service may not have started.\n", pn);
2958     }
2959     else if (code == ESRCH) {
2960         fprintf(stderr,"%s: Cell name not recognized.\n", pn);
2961     }
2962     else if (code == EPIPE) {
2963         fprintf(stderr,"%s: Volume name or ID not recognized.\n", pn);
2964     }
2965     else if (code == EFBIG) {
2966         fprintf(stderr,"%s: Cache size too large.\n", pn);
2967     }
2968     else if (code == ETIMEDOUT) {
2969         if (filename)
2970             fprintf(stderr,"%s:'%s': Connection timed out", pn, filename);
2971         else
2972             fprintf(stderr,"%s: Connection timed out", pn);
2973     }
2974     else {
2975         if (filename) fprintf(stderr,"%s:'%s'", pn, filename);
2976         else fprintf(stderr,"%s", pn);
2977 #ifdef WIN32
2978         fprintf(stderr, ": code 0x%x\n", code);
2979 #else /* not WIN32 */
2980         fprintf(stderr,": %s\n", error_message(code));
2981 #endif /* not WIN32 */
2982     }
2983 } /*Die*/
2984
2985 static MemDumpCmd(struct cmd_syndesc *asp)
2986 {
2987     long code;
2988     struct ViceIoctl blob;
2989     long inValue;
2990     long outValue;
2991   
2992     if ((asp->parms[0].items && asp->parms[1].items)) {
2993         fprintf(stderr, "fs trace: must use at most one of '-begin' or '-end'\n");
2994         return EINVAL;
2995     }
2996   
2997     /* determine if we're turning this tracing on or off */
2998     inValue = 0;
2999     if (asp->parms[0].items)
3000         inValue = 1;            /* begin */
3001     else if (asp->parms[1].items) 
3002         inValue = 0;            /* end */
3003   
3004     blob.in_size = sizeof(long);
3005     blob.in = (char *) &inValue;
3006     blob.out_size = sizeof(long);
3007     blob.out = (char *) &outValue;
3008
3009     code = pioctl(NULL, VIOC_TRACEMEMDUMP, &blob, 1);
3010     if (code) {
3011         Die(errno, NULL);
3012         return code;
3013     }
3014
3015     if (outValue) printf("AFS memdump begin.\n");
3016     else printf("AFS memdump end.\n");
3017
3018     return 0;
3019 }
3020