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