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