Windows: fs examine owner and group are signed
[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 #include <afs/com_err.h>
13
14 #include <windows.h>
15 #include <stdlib.h>
16 #include <malloc.h>
17 #include <string.h>
18 #include <stdio.h>
19 #include <time.h>
20 #include <winsock2.h>
21 #include <errno.h>
22 #include <assert.h>
23 #include <rx/rx_globals.h>
24
25 #include <osi.h>
26 #include <afsint.h>
27 #include <afs/afs_consts.h>
28 #include <afs/cellconfig.h>
29 #include <afs/ptserver.h>
30 #include <afs/ptuser.h>
31 #include <afs/volser.h>
32 #include <WINNT\afsreg.h>
33
34 #include "fs.h"
35 #include "fs_utils.h"
36 #include "cmd.h"
37 #include "afsd.h"
38 #include "cm_ioctl.h"
39
40 #define MAXNAME 100
41 #define MAXINSIZE 1300    /* pioctl complains if data is larger than this */
42 #define VMSGSIZE 128      /* size of msg buf in volume hdr */
43 #define CELL_MAXNAMELEN         256
44 #define MAXHOSTCHARS            64
45
46 static char space[AFS_PIOCTL_MAXSIZE];
47 static char tspace[1024];
48
49 static struct ubik_client *uclient;
50
51 static int GetClientAddrsCmd(struct cmd_syndesc *asp, void *arock);
52 static int SetClientAddrsCmd(struct cmd_syndesc *asp, void *arock);
53 static int FlushMountCmd(struct cmd_syndesc *asp, void *arock);
54 static int RxStatProcCmd(struct cmd_syndesc *asp, void *arock);
55 static int RxStatPeerCmd(struct cmd_syndesc *asp, void *arock);
56
57 extern struct cmd_syndesc *cmd_CreateSyntax();
58
59 static int MemDumpCmd(struct cmd_syndesc *asp, void *arock);
60 static int CSCPolicyCmd(struct cmd_syndesc *asp, void *arock);
61 static int MiniDumpCmd(struct cmd_syndesc *asp, void *arock);
62
63 static char pn[] = "fs";
64 static int rxInitDone = 0;
65
66 /*
67  * Character to use between name and rights in printed representation for
68  * DFS ACL's.
69  */
70 #define DFS_SEPARATOR   ' '
71
72 typedef char sec_rgy_name_t[1025];      /* A DCE definition */
73
74 struct Acl {
75     int dfs;            /* Originally true if a dfs acl; now also the type
76                          * of the acl (1, 2, or 3, corresponding to object,
77                          * initial dir, or initial object). */
78     sec_rgy_name_t cell; /* DFS cell name */
79     int nplus;
80     int nminus;
81     struct AclEntry *pluslist;
82     struct AclEntry *minuslist;
83 };
84
85 struct AclEntry {
86     struct AclEntry *next;
87     char name[MAXNAME];
88     afs_int32 rights;
89 };
90
91 static void 
92 ZapAcl (struct Acl *acl)
93 {
94     if (!acl)
95         return;
96
97     ZapList(acl->pluslist);
98     ZapList(acl->minuslist);
99     free(acl);
100 }
101
102 /*
103  * Mods for the AFS/DFS protocol translator.
104  *
105  * DFS rights. It's ugly to put these definitions here, but they 
106  * *cannot* change, because they're part of the wire protocol.
107  * In any event, the protocol translator will guarantee these
108  * assignments for AFS cache managers.
109  */
110 #define DFS_READ          0x01
111 #define DFS_WRITE         0x02
112 #define DFS_EXECUTE       0x04
113 #define DFS_CONTROL       0x08
114 #define DFS_INSERT        0x10
115 #define DFS_DELETE        0x20
116
117 /* the application definable ones (backwards from AFS) */
118 #define DFS_USR0 0x80000000      /* "A" bit */
119 #define DFS_USR1 0x40000000      /* "B" bit */
120 #define DFS_USR2 0x20000000      /* "C" bit */
121 #define DFS_USR3 0x10000000      /* "D" bit */
122 #define DFS_USR4 0x08000000      /* "E" bit */
123 #define DFS_USR5 0x04000000      /* "F" bit */
124 #define DFS_USR6 0x02000000      /* "G" bit */
125 #define DFS_USR7 0x01000000      /* "H" bit */
126 #define DFS_USRALL      (DFS_USR0 | DFS_USR1 | DFS_USR2 | DFS_USR3 |\
127                          DFS_USR4 | DFS_USR5 | DFS_USR6 | DFS_USR7)
128
129 /*
130  * Offset of -id switch in command structure for various commands.
131  * The -if switch is the next switch always.
132  */
133 static int parm_setacl_id, parm_copyacl_id, parm_listacl_id;
134
135 /*
136  * Determine whether either the -id or -if switches are present, and
137  * return 0, 1 or 2, as appropriate. Abort if both switches are present.
138  */
139 /* int id; Offset of -id switch; -if is next switch */
140 static int 
141 getidf(struct cmd_syndesc *as, int id)
142 {
143     int idf = 0;
144
145     if (as->parms[id].items) {
146         idf |= 1;
147     }
148     if (as->parms[id + 1].items) {
149         idf |= 2;
150     }
151     if (idf == 3) {
152         fprintf(stderr,
153              "%s: you may specify either -id or -if, but not both switches\n",
154              pn);
155         exit(1);
156     }
157     return idf;
158 }
159
160 static int
161 PRights(afs_int32 arights, int dfs)
162 {
163     if (!dfs) {
164         if (arights & PRSFS_READ) 
165             printf("r");
166         if (arights & PRSFS_LOOKUP) 
167             printf("l");
168         if (arights & PRSFS_INSERT) 
169             printf("i");
170         if (arights & PRSFS_DELETE) 
171             printf("d");
172         if (arights & PRSFS_WRITE) 
173             printf("w");
174         if (arights & PRSFS_LOCK) 
175             printf("k");
176         if (arights & PRSFS_ADMINISTER) 
177             printf("a");
178         if (arights & PRSFS_USR0) 
179             printf("A");
180         if (arights & PRSFS_USR1) 
181             printf("B");
182         if (arights & PRSFS_USR2) 
183             printf("C");
184         if (arights & PRSFS_USR3) 
185             printf("D");
186         if (arights & PRSFS_USR4) 
187             printf("E");
188         if (arights & PRSFS_USR5) 
189             printf("F");
190         if (arights & PRSFS_USR6) 
191             printf("G");
192         if (arights & PRSFS_USR7) 
193             printf("H");
194     } else {
195         if (arights & DFS_READ) 
196             printf("r");
197         else 
198             printf("-");
199         if (arights & DFS_WRITE) 
200             printf("w"); 
201         else 
202             printf("-");
203         if (arights & DFS_EXECUTE) 
204             printf("x"); 
205         else 
206             printf("-");
207         if (arights & DFS_CONTROL) 
208             printf("c"); 
209         else 
210             printf("-");
211         if (arights & DFS_INSERT) 
212             printf("i"); 
213         else 
214             printf("-");
215         if (arights & DFS_DELETE) 
216             printf("d"); 
217         else 
218             printf("-");
219         if (arights & (DFS_USRALL)) 
220             printf("+");
221         if (arights & DFS_USR0) 
222             printf("A");
223         if (arights & DFS_USR1) 
224             printf("B");
225         if (arights & DFS_USR2) 
226             printf("C");
227         if (arights & DFS_USR3) 
228             printf("D");
229         if (arights & DFS_USR4) 
230             printf("E");
231         if (arights & DFS_USR5) 
232             printf("F");
233         if (arights & DFS_USR6) 
234             printf("G");
235         if (arights & DFS_USR7) 
236             printf("H");
237     }   
238     return 0;
239 }
240
241 /* this function returns TRUE (1) if the file is in AFS, otherwise false (0) */
242 static int 
243 InAFS(char *apath)
244 {
245     struct ViceIoctl blob;
246     cm_ioctlQueryOptions_t options;
247     cm_fid_t fid;
248     afs_int32 code;
249
250     memset(&options, 0, sizeof(options));
251     options.size = sizeof(options);
252     options.field_flags |= CM_IOCTL_QOPTS_FIELD_LITERAL;
253     options.literal = 1;
254     blob.in_size = options.size;    /* no variable length data */
255     blob.in = &options;
256     blob.out_size = sizeof(cm_fid_t);
257     blob.out = (char *) &fid;
258
259     code = pioctl_utf8(apath, VIOCGETFID, &blob, 1);
260     if (code) {
261         if ((errno == EINVAL) || (errno == ENOENT)) 
262             return 0;
263     }
264     return 1;
265 }
266
267 static int 
268 IsFreelanceRoot(char *apath)
269 {
270     struct ViceIoctl blob;
271     afs_int32 code;
272
273     blob.in_size = 0;
274     blob.out_size = AFS_PIOCTL_MAXSIZE;
275     blob.out = space;
276
277     code = pioctl_utf8(apath, VIOC_FILE_CELL_NAME, &blob, 1);
278     if (code == 0) {
279         return !cm_strnicmp_utf8N("Freelance.Local.Root",space, blob.out_size);
280     }
281     return 1;   /* assume it is because it is more restrictive that way */
282 }
283
284 /* return a static pointer to a buffer */
285 static char *
286 Parent(char *apath)
287 {
288     char *tp;
289     strcpy(tspace, apath);
290     tp = strrchr(tspace, '\\');
291     if (tp) {
292         *(tp+1) = 0;    /* lv trailing slash so Parent("k:\foo") is "k:\" not "k:" */
293     }
294     else {
295         fs_ExtractDriveLetter(apath, tspace);
296         strcat(tspace, ".");
297     }
298     return tspace;
299 }
300
301 enum rtype {add, destroy, deny};
302
303 static afs_int32 
304 Convert(char *arights, int dfs, enum rtype *rtypep)
305 {
306     int i, len;
307     afs_int32 mode;
308     char tc;
309
310     *rtypep = add;      /* add rights, by default */
311
312     if (dfs) {
313         if (!strcmp(arights, "null")) {
314             *rtypep = deny;
315             return 0;
316         }
317         if (!strcmp(arights,"read")) 
318             return DFS_READ | DFS_EXECUTE;
319         if (!strcmp(arights, "write")) 
320             return DFS_READ | DFS_EXECUTE | DFS_INSERT | DFS_DELETE | 
321                 DFS_WRITE;
322         if (!strcmp(arights, "all")) 
323             return DFS_READ | DFS_EXECUTE | DFS_INSERT | DFS_DELETE | 
324                 DFS_WRITE | DFS_CONTROL;
325     } else {
326         if (!strcmp(arights,"read")) 
327             return PRSFS_READ | PRSFS_LOOKUP;
328         if (!strcmp(arights, "write")) 
329             return PRSFS_READ | PRSFS_LOOKUP | PRSFS_INSERT | PRSFS_DELETE | 
330                 PRSFS_WRITE | PRSFS_LOCK;
331         if (!strcmp(arights, "mail")) 
332             return PRSFS_INSERT | PRSFS_LOCK | PRSFS_LOOKUP;
333         if (!strcmp(arights, "all")) 
334             return PRSFS_READ | PRSFS_LOOKUP | PRSFS_INSERT | PRSFS_DELETE | 
335                 PRSFS_WRITE | PRSFS_LOCK | PRSFS_ADMINISTER;
336     }
337     if (!strcmp(arights, "none")) {
338         *rtypep = destroy; /* Remove entire entry */
339         return 0;
340     }
341     len = (int)strlen(arights);
342     mode = 0;
343     for(i=0;i<len;i++) {
344         tc = *arights++;
345         if (dfs) {
346             if (tc == '-') 
347                 continue;
348             else if (tc == 'r') 
349                 mode |= DFS_READ;
350             else if (tc == 'w') 
351                 mode |= DFS_WRITE;
352             else if (tc == 'x') 
353                 mode |= DFS_EXECUTE;
354             else if (tc == 'c') 
355                 mode |= DFS_CONTROL;
356             else if (tc == 'i') 
357                 mode |= DFS_INSERT;
358             else if (tc == 'd') 
359                 mode |= DFS_DELETE;
360             else if (tc == 'A') 
361                 mode |= DFS_USR0;
362             else if (tc == 'B') 
363                 mode |= DFS_USR1;
364             else if (tc == 'C') 
365                 mode |= DFS_USR2;
366             else if (tc == 'D') 
367                 mode |= DFS_USR3;
368             else if (tc == 'E') 
369                 mode |= DFS_USR4;
370             else if (tc == 'F') 
371                 mode |= DFS_USR5;
372             else if (tc == 'G') 
373                 mode |= DFS_USR6;
374             else if (tc == 'H') 
375                 mode |= DFS_USR7;
376             else {
377                 fprintf(stderr, "%s: illegal DFS rights character '%c'.\n", 
378                          pn, tc);
379                 exit(1);
380             }
381         } else {
382             if (tc == 'r') 
383                 mode |= PRSFS_READ;
384             else if (tc == 'l') 
385                 mode |= PRSFS_LOOKUP;
386             else if (tc == 'i') 
387                 mode |= PRSFS_INSERT;
388             else if (tc == 'd') 
389                 mode |= PRSFS_DELETE;
390             else if (tc == 'w') 
391                 mode |= PRSFS_WRITE;
392             else if (tc == 'k') 
393                 mode |= PRSFS_LOCK;
394             else if (tc == 'a') 
395                 mode |= PRSFS_ADMINISTER;
396             else if (tc == 'A') 
397                 mode |= PRSFS_USR0;
398             else if (tc == 'B') 
399                 mode |= PRSFS_USR1;
400             else if (tc == 'C') 
401                 mode |= PRSFS_USR2;
402             else if (tc == 'D') 
403                 mode |= PRSFS_USR3;
404             else if (tc == 'E') 
405                 mode |= PRSFS_USR4;
406             else if (tc == 'F') 
407                 mode |= PRSFS_USR5;
408             else if (tc == 'G') 
409                 mode |= PRSFS_USR6;
410             else if (tc == 'H') 
411                 mode |= PRSFS_USR7;
412             else {
413                 fprintf(stderr, "%s: illegal rights character '%c'.\n", pn, 
414                          tc);
415                 exit(1);
416             }
417         }
418     }
419     return mode;
420 }
421
422 static struct AclEntry *
423 FindList (struct AclEntry *alist, char *aname)
424 {
425     while (alist) {
426         if (!strcasecmp(alist->name, aname)) 
427             return alist;
428         alist = alist->next;
429     }
430     return 0;
431 }
432
433 /* if no parm specified in a particular slot, set parm to be "." instead */
434 static void 
435 SetDotDefault(struct cmd_item **aitemp)
436 {
437     struct cmd_item *ti;
438     if (*aitemp) 
439         return;                 /* already has value */
440     /* otherwise, allocate an item representing "." */
441     ti = (struct cmd_item *) malloc(sizeof(struct cmd_item));
442     assert(ti);
443     ti->next = (struct cmd_item *) 0;
444     ti->data = (char *) malloc(2);
445     assert(ti->data);
446     strcpy(ti->data, ".");
447     *aitemp = ti;
448 }
449
450 static void 
451 ChangeList (struct Acl *al, afs_int32 plus, char *aname, afs_int32 arights)
452 {
453     struct AclEntry *tlist;
454     tlist = (plus ? al->pluslist : al->minuslist);
455     tlist = FindList (tlist, aname);
456     if (tlist) {
457         /* Found the item already in the list. */
458         tlist->rights = arights;
459         if (plus)
460             al->nplus -= PruneList(&al->pluslist, al->dfs);
461         else
462             al->nminus -= PruneList(&al->minuslist, al->dfs);
463         return;
464     }
465     /* Otherwise we make a new item and plug in the new data. */
466     tlist = (struct AclEntry *) malloc(sizeof (struct AclEntry));
467     assert(tlist);
468     strcpy(tlist->name, aname);
469     tlist->rights = arights;
470     if (plus) {
471         tlist->next = al->pluslist;
472         al->pluslist = tlist;
473         al->nplus++;
474         if (arights == 0 || arights == -1)
475             al->nplus -= PruneList(&al->pluslist, al->dfs);
476     } else {
477         tlist->next = al->minuslist;
478         al->minuslist = tlist;
479         al->nminus++;
480         if (arights == 0) 
481             al->nminus -= PruneList(&al->minuslist, al->dfs);
482     }
483 }
484
485 static void 
486 ZapList (struct AclEntry *alist)
487 {
488     struct AclEntry *tp, *np;
489     for (tp = alist; tp; tp = np) {
490         np = tp->next;
491         free(tp);
492     }
493 }
494
495 static int 
496 PruneList (struct AclEntry **ae, int dfs)
497 {
498     struct AclEntry **lp;
499     struct AclEntry *te, *ne;
500     afs_int32 ctr;
501     ctr = 0;
502     lp = ae;
503     for(te = *ae;te;te=ne) {
504         if ((!dfs && te->rights == 0) || te->rights == -1) {
505             *lp = te->next;
506             ne = te->next;
507             free(te);
508             ctr++;
509         } else {
510             ne = te->next;
511             lp = &te->next;
512         }
513     }
514     return ctr;
515 }
516
517 static char *
518 SkipLine (char *astr)
519 {
520     while (*astr !='\n') 
521         astr++;
522     astr++;
523     return astr;
524 }
525
526 /*
527  * Create an empty acl, taking into account whether the acl pointed
528  * to by astr is an AFS or DFS acl. Only parse this minimally, so we
529  * can recover from problems caused by bogus ACL's (in that case, always
530  * assume that the acl is AFS: for DFS, the user can always resort to
531  * acl_edit, but for AFS there may be no other way out).
532  */
533 static struct Acl *
534 EmptyAcl(char *astr)
535 {
536     struct Acl *tp;
537     int junk;
538
539     tp = (struct Acl *)malloc(sizeof (struct Acl));
540     assert(tp);
541     tp->nplus = tp->nminus = 0;
542     tp->pluslist = tp->minuslist = 0;
543     tp->dfs = 0;
544     if (astr == NULL || sscanf(astr, "%d dfs:%d %s", &junk, &tp->dfs, tp->cell) <= 0) {
545         tp->dfs = 0;
546         tp->cell[0] = '\0';
547     }
548     return tp;
549 }
550
551 static struct Acl *
552 ParseAcl (char *astr)
553 {
554     int nplus, nminus, i, trights, ret;
555     char tname[MAXNAME];
556     struct AclEntry *first, *next, *last, *tl;
557     struct Acl *ta;
558
559     ta = EmptyAcl(NULL);
560     if (astr == NULL || strlen(astr) == 0)
561         return ta;
562
563     ret = sscanf(astr, "%d dfs:%d %s", &ta->nplus, &ta->dfs, ta->cell);
564     if (ret <= 0) {
565         free(ta);
566         return NULL;
567     }
568     astr = SkipLine(astr);
569     ret = sscanf(astr, "%d", &ta->nminus);
570     if (ret <= 0) {
571         free(ta);
572         return NULL;
573     }
574     astr = SkipLine(astr);
575
576     nplus = ta->nplus;
577     nminus = ta->nminus;
578
579     last = 0;
580     first = 0;
581     for(i=0;i<nplus;i++) {
582         ret = sscanf(astr, "%100s %d", tname, &trights); 
583         if (ret <= 0)
584             goto nplus_err;
585         astr = SkipLine(astr);
586         tl = (struct AclEntry *) malloc(sizeof (struct AclEntry));
587         if (tl == NULL)
588             goto nplus_err;
589         if (!first) 
590             first = tl;
591         strcpy(tl->name, tname);
592         tl->rights = trights;
593         tl->next = 0;
594         if (last) 
595             last->next = tl;
596         last = tl;
597     }
598     ta->pluslist = first;
599
600     last = 0;
601     first = 0;
602     for(i=0;i<nminus;i++) {
603         ret = sscanf(astr, "%100s %d", tname, &trights);
604         if (ret <= 0)
605             goto nminus_err;
606         astr = SkipLine(astr);
607         tl = (struct AclEntry *) malloc(sizeof (struct AclEntry));
608         if (tl == NULL)
609             goto nminus_err;
610         if (!first) 
611             first = tl;
612         strcpy(tl->name, tname);
613         tl->rights = trights;
614         tl->next = 0;
615         if (last) 
616             last->next = tl;
617         last = tl;
618     }
619     ta->minuslist = first;
620
621     return ta;
622
623   nminus_err:
624     for (;first; first = next) {
625         next = first->next;
626         free(first);
627     }   
628     first = ta->pluslist;
629
630   nplus_err:
631     for (;first; first = next) {
632         next = first->next;
633         free(first);
634     }   
635     free(ta);
636     return NULL;
637 }
638
639 static int
640 PrintStatus(VolumeStatus *status, char *name, char *motd, char *offmsg)
641 {
642     printf("Volume status for vid = %u named %s is\n",status->Vid, name);
643     if (*offmsg != 0)
644         printf("Current offline message is %s\n",offmsg);
645     if (*motd != 0)
646         printf("Current message of the day is %s\n",motd);
647     printf("Current disk quota is ");
648     if (status->MaxQuota != 0) 
649         printf("%d\n", status->MaxQuota);
650     else 
651         printf("unlimited\n");
652     printf("Current blocks used are %d\n",status->BlocksInUse);
653     printf("The partition has %d blocks available out of %d\n",
654             status->PartBlocksAvail, status->PartMaxBlocks);
655     return 0;
656 }
657
658 static int
659 QuickPrintStatus(VolumeStatus *status, char *name)
660 {
661     double QuotaUsed =0.0;
662     double PartUsed =0.0;
663     int WARN = 0;
664     printf("%-25.25s",name);
665
666     if (status->MaxQuota != 0) {
667         printf(" %10d %10d", status->MaxQuota, status->BlocksInUse);
668         QuotaUsed = ((((double)status->BlocksInUse)/status->MaxQuota) * 100.0);
669     } else {
670         printf("   no limit %10d", status->BlocksInUse);
671     }
672     if (QuotaUsed > 90.0){
673         printf(" %5.0f%%<<", QuotaUsed);
674         WARN = 1;
675     } else 
676         printf(" %5.0f%%  ", QuotaUsed);
677     PartUsed = (100.0 - ((((double)status->PartBlocksAvail)/status->PartMaxBlocks) * 100.0));
678     if (PartUsed > 97.0){
679         printf(" %9.0f%%<<", PartUsed);
680         WARN = 1;
681     } else 
682         printf(" %9.0f%%  ", PartUsed);
683     if (WARN){
684         printf("  <<WARNING\n");
685     } else 
686         printf("\n");
687     return 0;
688 }
689
690 static int
691 QuickPrintSpace(VolumeStatus *status, char *name)
692 {
693     double PartUsed =0.0;
694     int WARN = 0;
695     printf("%-25.25s",name);
696
697     printf("%10d%10d%10d", status->PartMaxBlocks, status->PartMaxBlocks - status->PartBlocksAvail, status->PartBlocksAvail);
698         
699     PartUsed = (100.0 - ((((double)status->PartBlocksAvail)/status->PartMaxBlocks) * 100.0));
700     if (PartUsed > 90.0){
701         printf(" %4.0f%%<<", PartUsed);
702         WARN = 1;
703     } else 
704         printf(" %4.0f%%  ", PartUsed);
705     if (WARN){
706         printf("  <<WARNING\n");
707     } else 
708         printf("\n");
709     return 0;
710 }
711
712 static char *
713 AclToString(struct Acl *acl)
714 {
715     static char mydata[AFS_PIOCTL_MAXSIZE];
716     char tstring[AFS_PIOCTL_MAXSIZE];
717     char dfsstring[30];
718     struct AclEntry *tp;
719     
720     if (acl->dfs) 
721         sprintf(dfsstring, " dfs:%d %s", acl->dfs, acl->cell);
722     else 
723         dfsstring[0] = '\0';
724     sprintf(mydata, "%d%s\n%d\n", acl->nplus, dfsstring, acl->nminus);
725     for (tp = acl->pluslist;tp;tp=tp->next) {
726         sprintf(tstring, "%s %d\n", tp->name, tp->rights);
727         strcat(mydata, tstring);
728     }
729     for (tp = acl->minuslist;tp;tp=tp->next) {
730         sprintf(tstring, "%s %d\n", tp->name, tp->rights);
731         strcat(mydata, tstring);
732     }
733     return mydata;
734 }
735
736 static DWORD IsFreelance(void)
737 {
738     HKEY  parmKey;
739     DWORD code;
740     DWORD dummyLen;
741     DWORD enabled = 0;
742
743     code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY,
744                          0, (IsWow64()?KEY_WOW64_64KEY:0)|KEY_QUERY_VALUE, &parmKey);
745     if (code == ERROR_SUCCESS) {
746         dummyLen = sizeof(cm_freelanceEnabled);
747         code = RegQueryValueEx(parmKey, "FreelanceClient", NULL, NULL,
748                             (BYTE *) &enabled, &dummyLen);
749         RegCloseKey (parmKey);
750     }
751     return enabled;
752 }
753
754 static const char * NetbiosName(void)
755 {
756     static char buffer[1024] = "AFS";
757     HKEY  parmKey;
758     DWORD code;
759     DWORD dummyLen;
760     DWORD enabled = 0;
761
762     code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY,
763                          0, (IsWow64()?KEY_WOW64_64KEY:0)|KEY_QUERY_VALUE, &parmKey);
764     if (code == ERROR_SUCCESS) {
765         dummyLen = sizeof(buffer);
766         code = RegQueryValueEx(parmKey, "NetbiosName", NULL, NULL,
767                                buffer, &dummyLen);
768         RegCloseKey (parmKey);
769     } else {
770         strcpy(buffer, "AFS");
771     }
772     return buffer;
773 }
774
775 #define AFSCLIENT_ADMIN_GROUPNAME "AFS Client Admins"
776
777 static BOOL IsAdmin (void)
778 {
779     static BOOL fAdmin = FALSE;
780     static BOOL fTested = FALSE;
781
782     if (!fTested)
783     {
784         /* Obtain the SID for the AFS client admin group.  If the group does
785          * not exist, then assume we have AFS client admin privileges.
786          */
787         PSID psidAdmin = NULL;
788         DWORD dwSize, dwSize2;
789         char pszAdminGroup[ MAX_COMPUTERNAME_LENGTH + sizeof(AFSCLIENT_ADMIN_GROUPNAME) + 2 ];
790         char *pszRefDomain = NULL;
791         SID_NAME_USE snu = SidTypeGroup;
792
793         dwSize = sizeof(pszAdminGroup);
794
795         if (!GetComputerName(pszAdminGroup, &dwSize)) {
796             /* Can't get computer name.  We return false in this case.
797                Retain fAdmin and fTested. This shouldn't happen.*/
798             return FALSE;
799         }
800
801         dwSize = 0;
802         dwSize2 = 0;
803
804         strcat(pszAdminGroup,"\\");
805         strcat(pszAdminGroup, AFSCLIENT_ADMIN_GROUPNAME);
806
807         LookupAccountName(NULL, pszAdminGroup, NULL, &dwSize, NULL, &dwSize2, &snu);
808         /* that should always fail. */
809
810         if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
811             /* if we can't find the group, then we allow the operation */
812             fAdmin = TRUE;
813             return TRUE;
814         }
815
816         if (dwSize == 0 || dwSize2 == 0) {
817             /* Paranoia */
818             fAdmin = TRUE;
819             return TRUE;
820         }
821
822         psidAdmin = (PSID)malloc(dwSize); memset(psidAdmin,0,dwSize);
823         assert(psidAdmin);
824         pszRefDomain = (char *)malloc(dwSize2);
825         assert(pszRefDomain);
826
827         if (!LookupAccountName(NULL, pszAdminGroup, psidAdmin, &dwSize, pszRefDomain, &dwSize2, &snu)) {
828             /* We can't lookup the group now even though we looked it up earlier.  
829                Could this happen? */
830             fAdmin = TRUE;
831         } else {
832             /* Then open our current ProcessToken */
833             HANDLE hToken;
834
835             if (OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken))
836             {
837
838                 if (!CheckTokenMembership(hToken, psidAdmin, &fAdmin)) {
839                     /* We'll have to allocate a chunk of memory to store the list of
840                      * groups to which this user belongs; find out how much memory
841                      * we'll need.
842                      */
843                     DWORD dwSize = 0;
844                     PTOKEN_GROUPS pGroups;
845
846                     GetTokenInformation (hToken, TokenGroups, NULL, dwSize, &dwSize);
847
848                     pGroups = (PTOKEN_GROUPS)malloc(dwSize);
849                     assert(pGroups);
850
851                     /* Allocate that buffer, and read in the list of groups. */
852                     if (GetTokenInformation (hToken, TokenGroups, pGroups, dwSize, &dwSize))
853                     {
854                         /* Look through the list of group SIDs and see if any of them
855                          * matches the AFS Client Admin group SID.
856                          */
857                         size_t iGroup = 0;
858                         for (; (!fAdmin) && (iGroup < pGroups->GroupCount); ++iGroup)
859                         {
860                             if (EqualSid (psidAdmin, pGroups->Groups[ iGroup ].Sid)) {
861                                 fAdmin = TRUE;
862                             }
863                         }
864                     }
865
866                     if (pGroups)
867                         free(pGroups);
868                 }
869
870                 /* if do not have permission because we were not explicitly listed
871                  * in the Admin Client Group let's see if we are the SYSTEM account
872                  */
873                 if (!fAdmin) {
874                     PTOKEN_USER pTokenUser;
875                     SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
876                     PSID pSidLocalSystem = 0;
877                     DWORD gle;
878
879                     GetTokenInformation(hToken, TokenUser, NULL, 0, &dwSize);
880
881                     pTokenUser = (PTOKEN_USER)malloc(dwSize);
882                     assert(pTokenUser);
883
884                     if (!GetTokenInformation(hToken, TokenUser, pTokenUser, dwSize, &dwSize))
885                         gle = GetLastError();
886
887                     if (AllocateAndInitializeSid( &SIDAuth, 1,
888                                                   SECURITY_LOCAL_SYSTEM_RID,
889                                                   0, 0, 0, 0, 0, 0, 0,
890                                                   &pSidLocalSystem))
891                     {
892                         if (EqualSid(pTokenUser->User.Sid, pSidLocalSystem)) {
893                             fAdmin = TRUE;
894                         }
895
896                         FreeSid(pSidLocalSystem);
897                     }
898
899                     if ( pTokenUser )
900                         free(pTokenUser);
901                 }
902             }
903         }
904
905         free(psidAdmin);
906         free(pszRefDomain);
907
908         fTested = TRUE;
909     }
910
911     return fAdmin;
912 }
913
914 static int
915 SetACLCmd(struct cmd_syndesc *as, void *arock)
916 {
917     afs_int32 code;
918     struct ViceIoctl blob;
919     struct Acl *ta = 0;
920     struct cmd_item *ti, *ui;
921     int plusp;
922     afs_int32 rights;
923     int clear;
924     int idf = getidf(as, parm_setacl_id);
925
926     int error = 0;
927
928     if (as->parms[2].items)
929         clear = 1;
930     else
931         clear = 0;
932     plusp = !(as->parms[3].items);
933     for(ti=as->parms[0].items; ti;ti=ti->next) {
934         blob.out_size = AFS_PIOCTL_MAXSIZE;
935         blob.in_size = idf;
936         blob.in = blob.out = space;
937         code = pioctl_utf8(ti->data, VIOCGETAL, &blob, 1);
938         if (code) {
939             Die(errno, ti->data);
940             error = 1;
941             continue;
942         }
943         if (ta)
944             ZapAcl(ta);
945         ta = ParseAcl(space);
946         if (!ta) {
947             fprintf(stderr,
948                     "fs: %s: invalid acl data returned from VIOCGETAL\n",
949                      ti->data);
950             error = 1;
951             continue;
952         }
953         if (!plusp && ta->dfs) {
954             fprintf(stderr,
955                     "fs: %s: you may not use the -negative switch with DFS acl's.\n%s",
956                     ti->data,
957                     "(you may specify \"null\" to revoke all rights, however)\n");
958             error = 1;
959             continue;
960         }
961         if (ta)
962             ZapAcl(ta);
963         if (clear) 
964             ta = EmptyAcl(space);
965         else 
966             ta = ParseAcl(space);
967         if (!ta) {
968             fprintf(stderr,
969                     "fs: %s: invalid acl data returned from VIOCGETAL\n",
970                      ti->data);
971             error = 1;
972             continue;
973         }
974         CleanAcl(ta, ti->data);
975         for(ui=as->parms[1].items; ui; ui=ui->next->next) {
976             enum rtype rtype;
977             if (!ui->next) {
978                 fprintf(stderr,
979                         "%s: Missing second half of user/access pair.\n", pn);
980                 ZapAcl(ta);
981                 return 1;
982             }
983             rights = Convert(ui->next->data, ta->dfs, &rtype);
984             if (rtype == destroy && !ta->dfs) {
985                 struct AclEntry *tlist;
986
987                 tlist = (plusp ? ta->pluslist : ta->minuslist);
988                 if (!FindList(tlist, ui->data))
989                     continue;
990             }
991             if (rtype == deny && !ta->dfs) 
992                 plusp = 0;
993             if (rtype == destroy && ta->dfs) 
994                 rights = -1;
995             ChangeList(ta, plusp, ui->data, rights);
996         }
997         blob.in = AclToString(ta);
998         blob.out_size=0;
999         blob.in_size = 1+(long)strlen(blob.in);
1000         code = pioctl_utf8(ti->data, VIOCSETAL, &blob, 1);
1001         if (code) {
1002             if (errno == EINVAL) {
1003                 if (ta->dfs) {
1004                     static char *fsenv = 0;
1005                     if (!fsenv) {
1006                         fsenv = (char *)getenv("FS_EXPERT");
1007                     }
1008                     fprintf(stderr, "fs: \"Invalid argument\" was returned when you tried to store a DFS access list.\n");
1009                     if (!fsenv) {
1010                         fprintf(stderr,
1011     "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
1012     "\nPossible reasons for this include:\n\n",                     
1013     " -You may have specified an inappropriate combination of rights.\n",
1014     "  For example, some DFS-supported filesystems may not allow you to\n",
1015     "  drop the \"c\" right from \"user_obj\".\n\n",
1016     " -A mask_obj may be required (it is likely required by the underlying\n",
1017     "  filesystem if you try to set anything other than the basic \"user_obj\"\n",
1018     "  \"mask_obj\", or \"group_obj\" entries). Unlike acl_edit, the fs command\n",
1019     "  does not automatically create or update the mask_obj. Try setting\n",
1020     "  the rights \"mask_obj all\" with \"fs sa\" before adding any explicit\n",
1021     "  users or groups. You can do this with a single command, such as\n",
1022     "  \"fs sa mask_obj all user:somename read\"\n\n",
1023     " -A specified user or group may not exist.\n\n",
1024     " -You may have tried to delete \"user_obj\", \"group_obj\", or \"other_obj\".\n",
1025     "  This is probably not allowed by the underlying file system.\n\n",
1026     " -If you add a user or group to a DFS ACL, remember that it must be\n",
1027     "  fully specified as \"user:username\" or \"group:groupname\". In addition, there\n",
1028     "  may be local requirements on the format of the user or group name.\n",
1029     "  Check with your cell administrator.\n\n",                            
1030     " -Or numerous other possibilities. It would be great if we could be more\n",
1031     "  precise about the actual problem, but for various reasons, this is\n",
1032     "  impractical via this interface.  If you can't figure it out, you\n",
1033     "  might try logging into a DCE-equipped machine and use acl_edit (or\n",
1034     "  whatever is provided). You may get better results. Good luck!\n\n",
1035     " (You may inhibit this message by setting \"FS_EXPERT\" in your environment)\n");
1036                     }
1037                 } else {
1038                     fprintf(stderr,
1039                             "%s: Invalid argument, possible reasons include:\n", 
1040                              pn);
1041                     fprintf(stderr,"\t-File not in AFS\n");
1042                     fprintf(stderr,
1043                             "\t-Too many users on access control list\n");
1044                     fprintf(stderr,
1045                             "\t-Tried to add non-existent user to access control list\n");
1046                 }
1047             } else {
1048                 Die(errno, ti->data);
1049             }
1050             error = 1;
1051         }
1052     }
1053     if (ta)
1054         ZapAcl(ta);
1055     return error;
1056 }
1057
1058 static int 
1059 CopyACLCmd(struct cmd_syndesc *as, void *arock)
1060 {
1061     afs_int32 code;
1062     struct ViceIoctl blob;
1063     struct Acl *fa, *ta = 0;
1064     struct AclEntry *tp;
1065     struct cmd_item *ti;
1066     int clear;
1067     int idf = getidf(as, parm_copyacl_id);
1068     int error = 0;
1069
1070     if (as->parms[2].items) 
1071         clear=1;
1072     else 
1073         clear=0;
1074     blob.out_size = AFS_PIOCTL_MAXSIZE;
1075     blob.in_size = idf;
1076     blob.in = blob.out = space;
1077     code = pioctl_utf8(as->parms[0].items->data, VIOCGETAL, &blob, 1);
1078     if (code) {
1079         Die(errno, as->parms[0].items->data);
1080         return 1;
1081     }
1082     fa = ParseAcl(space);
1083     if (!fa) {
1084         fprintf(stderr,
1085                  "fs: %s: invalid acl data returned from VIOCGETAL\n",
1086                  as->parms[0].items->data);
1087         return 1;
1088     }
1089     CleanAcl(fa, as->parms[0].items->data);
1090     for (ti=as->parms[1].items; ti;ti=ti->next) {
1091         blob.out_size = AFS_PIOCTL_MAXSIZE;
1092         blob.in_size = idf;
1093         blob.in = blob.out = space;
1094         code = pioctl_utf8(ti->data, VIOCGETAL, &blob, 1);
1095         if (code) {
1096             Die(errno, ti->data);
1097             error = 1;
1098             continue;
1099         }
1100         if (ta)
1101             ZapAcl(ta);
1102         if (clear) 
1103             ta = EmptyAcl(space);
1104         else 
1105             ta = ParseAcl(space);
1106         if (!ta) {
1107             fprintf(stderr,
1108                     "fs: %s: invalid acl data returned from VIOCGETAL\n",
1109                      ti->data);
1110             error = 1;
1111             continue;
1112         }
1113         CleanAcl(ta, ti->data);
1114         if (ta->dfs != fa->dfs) {
1115             fprintf(stderr, 
1116                     "%s: incompatible file system types: acl not copied to %s; aborted\n", 
1117                     pn, ti->data);
1118             error = 1;
1119             continue;
1120         }
1121         if (ta->dfs) {
1122             if (! clear && strcmp(ta->cell, fa->cell) != 0) {
1123                 fprintf(stderr, 
1124                         "%s: default DCE cell differs for file %s: use \"-clear\" switch; acl not merged\n", 
1125                         pn, ti->data);
1126                 error = 1;
1127                 continue;
1128             }
1129             strcpy(ta->cell, fa->cell);
1130         }
1131         for (tp = fa->pluslist;tp;tp=tp->next) 
1132             ChangeList(ta, 1, tp->name, tp->rights);
1133         for (tp = fa->minuslist;tp;tp=tp->next) 
1134             ChangeList(ta, 0, tp->name, tp->rights);
1135         blob.in = AclToString(ta);
1136         blob.out_size=0;
1137         blob.in_size = 1+(long)strlen(blob.in);
1138         code = pioctl_utf8(ti->data, VIOCSETAL, &blob, 1);
1139         if (code) {
1140             if (errno == EINVAL) {
1141                 fprintf(stderr,
1142                         "%s: Invalid argument, possible reasons include:\n", pn);
1143                 fprintf(stderr,"\t-File not in AFS\n");
1144             } else {
1145                 Die(errno, ti->data);
1146             }
1147             error = 1;
1148         }
1149     } 
1150     if (ta)
1151         ZapAcl(ta);
1152     ZapAcl(fa);
1153     return error;
1154 }
1155
1156 /* pioctl_utf8() call to get the cellname of a pathname */
1157 static afs_int32
1158 GetCell(char *fname, char *cellname)
1159 {
1160     afs_int32 code;
1161     struct ViceIoctl blob;
1162
1163     blob.in_size = 0;
1164     blob.out_size = CELL_MAXNAMELEN;
1165     blob.out = cellname;
1166
1167     code = pioctl_utf8(fname, VIOC_FILE_CELL_NAME, &blob, 1);
1168     if (code == 0)
1169         cellname[blob.out_size - 1] = '\0';
1170     return code;
1171 }
1172
1173 /* Check if a username is valid: If it contains only digits (or a
1174  * negative sign), then it might be bad.  We then query the ptserver
1175  * to see.
1176  */
1177 static int
1178 BadName(char *aname, char *fname)
1179 {
1180     afs_int32 tc, code, id;
1181     char *nm;
1182     char cell[CELL_MAXNAMELEN];
1183     char confDir[257];
1184
1185     for ( nm = aname; tc = *nm; nm++) {
1186         /* all must be '-' or digit to be bad */
1187         if (tc != '-' && (tc < '0' || tc > '9'))
1188             return 0;
1189     }
1190
1191     /* Go to the PRDB and see if this all number username is valid */
1192     code = GetCell(fname, cell);
1193     if (code)
1194         return 0;
1195
1196     cm_GetConfigDir(confDir, sizeof(confDir));
1197
1198     pr_Initialize(1, confDir, cell);
1199     code = pr_SNameToId(aname, &id);
1200     pr_End();
1201
1202     /* 1=>Not-valid; 0=>Valid */
1203     return ((!code && (id == ANONYMOUSID)) ? 1 : 0);
1204 }
1205
1206
1207 /* clean up an access control list of its bad entries; return 1 if we made
1208    any changes to the list, and 0 otherwise */
1209 static int 
1210 CleanAcl(struct Acl *aa, char *fname)
1211 {
1212     struct AclEntry *te, **le, *ne;
1213     int changes;
1214
1215     /* Don't correct DFS ACL's for now */
1216     if (aa->dfs)
1217         return 0;
1218
1219     /* prune out bad entries */
1220     changes = 0;            /* count deleted entries */
1221     le = &aa->pluslist;
1222     for(te = aa->pluslist; te; te=ne) {
1223         ne = te->next;
1224         if (BadName(te->name, fname)) {
1225             /* zap this dude */
1226             *le = te->next;
1227             aa->nplus--;
1228             free(te);
1229             changes++;
1230         } else {
1231             le = &te->next;
1232         }
1233     }
1234     le = &aa->minuslist;
1235     for(te = aa->minuslist; te; te=ne) {
1236         ne = te->next;
1237         if (BadName(te->name, fname)) {
1238             /* zap this dude */
1239             *le = te->next;
1240             aa->nminus--;
1241             free(te);
1242             changes++;
1243         } else {
1244             le = &te->next;
1245         }
1246     }
1247     return changes;
1248 }
1249
1250
1251 /* clean up an acl to not have bogus entries */
1252 static int 
1253 CleanACLCmd(struct cmd_syndesc *as, void *arock)
1254 {
1255     afs_int32 code;
1256     struct Acl *ta = 0;
1257     struct ViceIoctl blob;
1258     int changes;
1259     struct cmd_item *ti;
1260     struct AclEntry *te;
1261     int error = 0;
1262
1263     SetDotDefault(&as->parms[0].items);
1264     for(ti=as->parms[0].items; ti; ti=ti->next) {
1265         blob.out_size = AFS_PIOCTL_MAXSIZE;
1266         blob.in_size = 0;
1267         blob.out = space;
1268         code = pioctl_utf8(ti->data, VIOCGETAL, &blob, 1);
1269         if (code) {
1270             Die(errno, ti->data);
1271             error = 1;
1272             continue;
1273         }
1274         if (ta)
1275             ZapAcl(ta);
1276         ta = ParseAcl(space);
1277         if (!ta) {
1278             fprintf(stderr,
1279                     "fs: %s: invalid acl data returned from VIOCGETAL\n",
1280                      ti->data);
1281             error = 1;
1282             continue;
1283         }
1284         if (ta->dfs) {
1285             fprintf(stderr,
1286                     "%s: cleanacl is not supported for DFS access lists.\n",
1287                     pn);
1288             error = 1;
1289             continue;
1290         }
1291
1292         changes = CleanAcl(ta, ti->data);
1293
1294         if (changes) {
1295             /* now set the acl */
1296             blob.in=AclToString(ta);
1297             blob.in_size = (long)strlen(blob.in)+1;
1298             blob.out_size = 0;
1299             code = pioctl_utf8(ti->data, VIOCSETAL, &blob, 1);
1300             if (code) {
1301                 if (errno == EINVAL) {
1302                     fprintf(stderr,
1303                             "%s: Invalid argument, possible reasons include\n", 
1304                              pn);
1305                     fprintf(stderr,"%s: File not in vice or\n", pn);
1306                     fprintf(stderr,
1307                             "%s: Too many users on access control list or\n", 
1308                             pn);
1309                 } else {
1310                     Die(errno, ti->data);
1311                 }
1312                 error = 1;
1313                 continue;
1314             }
1315
1316             /* now list the updated acl */
1317             printf("Access list for %s is now\n", ti->data);
1318             if (ta->nplus > 0) {
1319                 if (!ta->dfs) 
1320                     printf("Normal rights:\n");
1321                 for(te = ta->pluslist;te;te=te->next) {
1322                     printf("  %s ", te->name);
1323                     PRights(te->rights, ta->dfs);
1324                     printf("\n");
1325                 }
1326             }
1327             if (ta->nminus > 0) {
1328                 printf("Negative rights:\n");
1329                 for(te = ta->minuslist;te;te=te->next) {
1330                     printf("  %s ", te->name);
1331                     PRights(te->rights, ta->dfs);
1332                     printf("\n");
1333                 }
1334             }
1335             if (ti->next) 
1336                 printf("\n");
1337         } else
1338             printf("Access list for %s is fine.\n", ti->data);
1339     }
1340     if (ta)
1341         ZapAcl(ta);
1342     return error;
1343 }
1344
1345 static int 
1346 ListACLCmd(struct cmd_syndesc *as, void *arock) 
1347 {
1348     afs_int32 code;
1349     struct Acl *ta = 0;
1350     struct ViceIoctl blob;
1351     struct AclEntry *te;
1352     struct cmd_item *ti;
1353     int idf = getidf(as, parm_listacl_id);
1354     int error = 0;
1355
1356     SetDotDefault(&as->parms[0].items);
1357     for(ti=as->parms[0].items; ti; ti=ti->next) {
1358         char separator;
1359         blob.out_size = AFS_PIOCTL_MAXSIZE;
1360         blob.in_size = idf;
1361         blob.in = blob.out = space;
1362         code = pioctl_utf8(ti->data, VIOCGETAL, &blob, 1);
1363         if (code) {
1364             Die(errno, ti->data);
1365             error = 1;
1366             continue;
1367         }
1368         ta = ParseAcl(space);
1369         if (!ta) {
1370             fprintf(stderr,
1371                     "fs: %s: invalid acl data returned from VIOCGETAL\n",
1372                      ti->data);
1373             error = 1;
1374             continue;
1375         }
1376         if (as->parms[3].items) {                       /* -cmd */
1377             printf("fs setacl -dir %s -acl ", ti->data);
1378             if (ta->nplus > 0) {
1379                 for (te = ta->pluslist; te; te = te->next) {
1380                     printf("  %s ", te->name);
1381                     PRights(te->rights, ta->dfs);
1382                 }
1383             }
1384             printf("\n");
1385             if (ta->nminus > 0) {
1386                 printf("fs setacl -dir %s -acl ", ti->data);
1387                 for (te = ta->minuslist; te; te = te->next) {
1388                     printf("  %s ", te->name);
1389                     PRights(te->rights, ta->dfs);
1390                 }
1391                 printf(" -negative\n");
1392             }
1393         } else {
1394             switch (ta->dfs) {
1395             case 0:
1396                 printf("Access list for %s is\n", ti->data);
1397                 break;
1398             case 1:
1399                 printf("DFS access list for %s is\n", ti->data);
1400                 break;
1401             case 2:
1402                 printf("DFS initial directory access list of %s is\n", ti->data);
1403                 break;
1404             case 3:
1405                 printf("DFS initial file access list of %s is\n", ti->data);
1406                 break;
1407             }
1408             if (ta->dfs) {
1409                 printf("  Default cell = %s\n", ta->cell);
1410             }
1411             separator = ta->dfs? DFS_SEPARATOR : ' ';
1412             if (ta->nplus > 0) {
1413                 if (!ta->dfs)
1414                     printf("Normal rights:\n");
1415                 for(te = ta->pluslist;te;te=te->next) {
1416                     printf("  %s%c", te->name, separator);
1417                     PRights(te->rights, ta->dfs);
1418                     printf("\n");
1419                 }
1420             }
1421             if (ta->nminus > 0) {
1422                 printf("Negative rights:\n");
1423                 for(te = ta->minuslist;te;te=te->next) {
1424                     printf("  %s ", te->name);
1425                     PRights(te->rights, ta->dfs);
1426                     printf("\n");
1427                 }
1428             }
1429             if (ti->next)
1430                 printf("\n");
1431         }
1432         ZapAcl(ta);
1433     }
1434     return error;
1435 }
1436
1437 static int
1438 FlushAllCmd(struct cmd_syndesc *as, void *arock)
1439 {
1440     afs_int32 code;
1441     struct ViceIoctl blob;
1442
1443     blob.in_size = blob.out_size = 0;
1444     code = pioctl_utf8(NULL, VIOC_FLUSHALL, &blob, 0);
1445     if (code) {
1446         fprintf(stderr, "Error flushing all ");
1447         return 1;
1448     }
1449     return 0;
1450 }
1451
1452 static int
1453 FlushVolumeCmd(struct cmd_syndesc *as, void *arock)
1454 {
1455     afs_int32 code;
1456     struct ViceIoctl blob;
1457     struct cmd_item *ti;
1458     int error = 0;
1459
1460     SetDotDefault(&as->parms[0].items);
1461     for(ti=as->parms[0].items; ti; ti=ti->next) {
1462         blob.in_size = blob.out_size = 0;
1463         code = pioctl_utf8(ti->data, VIOC_FLUSHVOLUME, &blob, 0);
1464         if (code) {
1465             fprintf(stderr, "Error flushing volume ");
1466             perror(ti->data);
1467             error = 1;
1468             continue;
1469         }
1470     }
1471     return error;
1472 }
1473
1474 static int 
1475 FlushCmd(struct cmd_syndesc *as, void *arock) 
1476 {
1477     afs_int32 code;
1478     struct ViceIoctl blob;
1479     struct cmd_item *ti;
1480     int error = 0;
1481     int literal = 0;
1482     cm_ioctlQueryOptions_t options;
1483
1484     if (as->parms[1].items)
1485         literal = 1;
1486     
1487     for(ti=as->parms[0].items; ti; ti=ti->next) {
1488         /* once per file */
1489         memset(&options, 0, sizeof(options));
1490         options.size = sizeof(options);
1491         options.field_flags |= CM_IOCTL_QOPTS_FIELD_LITERAL;
1492         options.literal = literal;
1493         blob.in_size = options.size;    /* no variable length data */
1494         blob.in = &options;
1495
1496         blob.out_size = 0;
1497         code = pioctl_utf8(ti->data, VIOCFLUSH, &blob, 0);
1498         if (code) {
1499             if (errno == EMFILE) {
1500                 fprintf(stderr, "%s: Can't flush active file %s\n", pn, 
1501                         ti->data);
1502             } else {
1503                 fprintf(stderr, "%s: Error flushing file ", pn);
1504                 perror(ti->data);
1505             }
1506             error = 1;
1507             continue;
1508         }
1509     }
1510     return error;
1511 }
1512
1513 /* all this command does is repackage its args and call SetVolCmd */
1514 static int
1515 SetQuotaCmd(struct cmd_syndesc *as, void *arock) {
1516     struct cmd_syndesc ts;
1517
1518     /* copy useful stuff from our command slot; we may later have to reorder */
1519     memcpy(&ts, as, sizeof(ts));        /* copy whole thing */
1520     return SetVolCmd(&ts, arock);
1521 }
1522
1523 static int
1524 SetVolCmd(struct cmd_syndesc *as, void *arock) {
1525     afs_int32 code;
1526     struct ViceIoctl blob;
1527     struct cmd_item *ti;
1528     struct VolumeStatus *status;
1529     char *motd, *offmsg, *input;
1530     int error = 0;
1531
1532     SetDotDefault(&as->parms[0].items);
1533     for(ti=as->parms[0].items; ti; ti=ti->next) {
1534         /* once per file */
1535         blob.out_size = AFS_PIOCTL_MAXSIZE;
1536         blob.in_size = sizeof(*status) + 3;     /* for the three terminating nulls */
1537         blob.out = space;
1538         blob.in = space;
1539         status = (VolumeStatus *)space;
1540         status->MinQuota = status->MaxQuota = -1;
1541         motd = offmsg = NULL;
1542         if (as->parms[1].items) {
1543             code = util_GetHumanInt32(as->parms[1].items->data, &status->MaxQuota);
1544             if (code) {
1545                 fprintf(stderr,"%s: bad integer specified for quota.\n", pn);
1546                 error = 1;
1547                 continue;
1548             }
1549         }
1550         if (as->parms[2].items) 
1551             motd = as->parms[2].items->data;
1552         if (as->parms[3].items) 
1553             offmsg = as->parms[3].items->data;
1554         input = (char *)status + sizeof(*status);
1555         *(input++) = '\0';      /* never set name: this call doesn't change vldb */
1556         if(offmsg) {
1557             if (strlen(offmsg) >= VMSGSIZE) {
1558                 fprintf(stderr,"%s: message must be shorter than %d characters\n",
1559                          pn, VMSGSIZE);
1560                 error = 1;
1561                 continue;
1562             }
1563             strcpy(input,offmsg);
1564             blob.in_size += (long)strlen(offmsg);
1565             input += strlen(offmsg) + 1;
1566         } else 
1567             *(input++) = '\0';
1568         if(motd) {
1569             if (strlen(motd) >= VMSGSIZE) {
1570                 fprintf(stderr,"%s: message must be shorter than %d characters\n",
1571                          pn, VMSGSIZE);
1572                 return code;
1573             }
1574             strcpy(input,motd);
1575             blob.in_size += (long)strlen(motd);
1576             input += strlen(motd) + 1;
1577         } else 
1578             *(input++) = '\0';
1579         code = pioctl_utf8(ti->data,VIOCSETVOLSTAT, &blob, 1);
1580         if (code) {
1581             Die(errno, ti->data);
1582             error = 1;
1583         }
1584     }
1585     return error;
1586 }
1587
1588 /* values match cache manager File Types */
1589 static char *
1590 filetypestr(afs_uint32 type)
1591 {
1592     char * s = "Object";
1593
1594     switch (type) {
1595     case 1:     /* file */
1596         s = "File";
1597         break;
1598     case 2:
1599         s = "Directory";
1600         break;
1601     case 3:
1602         s = "Symlink";
1603         break;
1604     case 4:
1605         s = "Mountpoint";
1606         break;
1607     case 5:
1608         s = "DfsLink";
1609         break;
1610     }
1611     return s;
1612 }
1613
1614 static int 
1615 ExamineCmd(struct cmd_syndesc *as, void *arock)
1616 {
1617     afs_int32 code;
1618     struct ViceIoctl blob;
1619     struct cmd_item *ti;
1620     struct VolumeStatus *status;
1621     char *name, *offmsg, *motd;
1622     int error = 0;
1623     int literal = 0;
1624     cm_ioctlQueryOptions_t options;
1625
1626     if (as->parms[1].items)
1627         literal = 1;
1628
1629     SetDotDefault(&as->parms[0].items);
1630     for(ti=as->parms[0].items; ti; ti=ti->next) {
1631         cm_fid_t fid;
1632         afs_uint32 filetype;
1633         afs_int32 owner[2];
1634         char cell[CELL_MAXNAMELEN];
1635
1636         /* once per file */
1637         memset(&fid, 0, sizeof(fid));
1638         memset(&options, 0, sizeof(options));
1639         filetype = 0;
1640         options.size = sizeof(options);
1641         options.field_flags |= CM_IOCTL_QOPTS_FIELD_LITERAL;
1642         options.literal = literal;
1643         blob.in_size = options.size;    /* no variable length data */
1644         blob.in = &options;
1645
1646         blob.out_size = sizeof(cm_fid_t);
1647         blob.out = (char *) &fid;
1648         if (0 == pioctl_utf8(ti->data, VIOCGETFID, &blob, 1) &&
1649             blob.out_size == sizeof(cm_fid_t)) {
1650             options.field_flags |= CM_IOCTL_QOPTS_FIELD_FID;
1651             options.fid = fid;
1652         } else {
1653             Die(errno, ti->data);
1654             error = 1;
1655             continue;
1656         }
1657
1658         blob.out_size = sizeof(filetype);
1659         blob.out = &filetype;
1660
1661         code = pioctl_utf8(ti->data, VIOC_GETFILETYPE, &blob, 1);
1662         if (code || blob.out_size != sizeof(filetype)) {
1663             Die(errno, ti->data);
1664             error = 1;
1665             continue;
1666         }
1667
1668         blob.out_size = CELL_MAXNAMELEN;
1669         blob.out = cell;
1670
1671         code = pioctl_utf8(ti->data, VIOC_FILE_CELL_NAME, &blob, 1);
1672         if (code == 0)
1673             cell[blob.out_size-1] = '\0';
1674         printf("%s %s (%u.%u.%u) contained in cell %s\n",
1675                 filetypestr(filetype),
1676                 ti->data, fid.volume, fid.vnode, fid.unique,
1677                 code ? "unknown-cell" : cell);
1678
1679         blob.out_size = 2 * sizeof(afs_uint32);
1680         blob.out = (char *) &owner;
1681         if (0 == pioctl_utf8(ti->data, VIOCGETOWNER, &blob, 1) &&
1682             blob.out_size == 2 * sizeof(afs_uint32)) {
1683             char oname[PR_MAXNAMELEN] = "(unknown)";
1684             char gname[PR_MAXNAMELEN] = "(unknown)";
1685             char confDir[257];
1686
1687             /* Go to the PRDB and see if this all number username is valid */
1688             cm_GetConfigDir(confDir, sizeof(confDir));
1689
1690             pr_Initialize(1, confDir, cell);
1691             pr_SIdToName(owner[0], oname);
1692             pr_SIdToName(owner[1], gname);
1693             printf("Owner %s (%d) Group %s (%d)\n", oname, owner[0], gname, owner[1]);
1694         }
1695
1696         blob.out = space;
1697         blob.out_size = AFS_PIOCTL_MAXSIZE;
1698         code = pioctl_utf8(ti->data, VIOCGETVOLSTAT, &blob, 1);
1699         if (code == 0) {
1700             space[blob.out_size - 1] = '\0';
1701             status = (VolumeStatus *)space;
1702             name = (char *)status + sizeof(*status);
1703             offmsg = name + strlen(name) + 1;
1704             motd = offmsg + strlen(offmsg) + 1;
1705             PrintStatus(status, name, motd, offmsg);
1706         } else {
1707             Die(errno, ti->data);
1708         }
1709
1710         errno = 0;
1711         code = pioctl_utf8(ti->data, VIOC_PATH_AVAILABILITY, &blob, 1);
1712         switch (errno) {
1713         case 0:
1714             printf("Volume is online\n");
1715             break;
1716         case ENXIO:
1717             printf("Volume is offline\n");
1718             break;
1719         case ENOSYS:
1720             printf("All Volume servers are down\n");
1721             break;
1722         case EBUSY:
1723             printf("All volume servers are busy\n");
1724             break;
1725         default:
1726             printf("Unknown volume state\n");
1727             Die(errno, ti->data);
1728         }
1729         printf("\n");
1730     }
1731     return error;
1732 }
1733
1734 static int
1735 ListQuotaCmd(struct cmd_syndesc *as, void *arock) 
1736 {
1737     afs_int32 code;
1738     struct ViceIoctl blob;
1739     struct cmd_item *ti;
1740     struct VolumeStatus *status;
1741     char *name;
1742
1743     int error = 0;
1744     
1745     printf("%-25s%-11s%-11s%-7s%-13s\n", "Volume Name", "      Quota",
1746            "       Used", "  %Used", "    Partition");
1747     SetDotDefault(&as->parms[0].items);
1748     for(ti=as->parms[0].items; ti; ti=ti->next) {
1749         /* once per file */
1750         blob.out_size = AFS_PIOCTL_MAXSIZE;
1751         blob.in_size = 0;
1752         blob.out = space;
1753         code = pioctl_utf8(ti->data, VIOCGETVOLSTAT, &blob, 1);
1754         if (code) {
1755             Die(errno, ti->data);
1756             error = 1;
1757             continue;
1758         }
1759         space[blob.out_size - 1] = '\0';
1760         status = (VolumeStatus *)space;
1761         name = (char *)status + sizeof(*status);
1762         QuickPrintStatus(status, name);
1763     }
1764     return error;
1765 }
1766
1767 static int
1768 WhereIsCmd(struct cmd_syndesc *as, void *arock)
1769 {
1770     afs_int32 code;
1771     struct ViceIoctl blob;
1772     struct cmd_item *ti;
1773     int j;
1774     afs_int32 *hosts;
1775     char *tp;
1776     int error = 0;
1777     int literal = 0;
1778     cm_ioctlQueryOptions_t options;
1779
1780     if (as->parms[1].items)
1781         literal = 1;
1782     
1783     SetDotDefault(&as->parms[0].items);
1784     for(ti=as->parms[0].items; ti; ti=ti->next) {
1785         cm_fid_t fid;
1786         afs_uint32 filetype;
1787
1788         /* once per file */
1789         memset(&fid, 0, sizeof(fid));
1790         memset(&options, 0, sizeof(options));
1791         filetype = 0;
1792         options.size = sizeof(options);
1793         options.field_flags |= CM_IOCTL_QOPTS_FIELD_LITERAL;
1794         options.literal = literal;
1795         blob.in_size = options.size;    /* no variable length data */
1796         blob.in = &options;
1797         
1798         blob.out_size = sizeof(cm_fid_t);
1799         blob.out = (char *) &fid;
1800         if (0 == pioctl_utf8(ti->data, VIOCGETFID, &blob, 1) &&
1801             blob.out_size == sizeof(cm_fid_t)) {
1802             options.field_flags |= CM_IOCTL_QOPTS_FIELD_FID;
1803             options.fid = fid;
1804         } else {
1805             Die(errno, ti->data);
1806             error = 1;
1807             continue;
1808         }
1809
1810         blob.out_size = sizeof(filetype);
1811         blob.out = &filetype;
1812
1813         code = pioctl_utf8(ti->data, VIOC_GETFILETYPE, &blob, 1);
1814         if (code || blob.out_size != sizeof(filetype)) {
1815             Die(errno, ti->data);
1816             error = 1;
1817             continue;
1818         }
1819         blob.out_size = AFS_PIOCTL_MAXSIZE;
1820         blob.out = space;
1821         memset(space, 0, sizeof(space));
1822         code = pioctl_utf8(ti->data, VIOCWHEREIS, &blob, 1);
1823         if (code) {
1824             Die(errno, ti->data);
1825             error = 1;
1826             continue;
1827         }
1828         hosts = (afs_int32 *) space;
1829         printf("%s %s is on host%s ", 
1830                 filetypestr(filetype),
1831                 ti->data,
1832                 (hosts[0] && !hosts[1]) ? "": "s");
1833         for(j=0; j<AFS_MAXHOSTS; j++) {
1834             if (hosts[j] == 0) 
1835                 break;
1836             tp = hostutil_GetNameByINet(hosts[j]);
1837             printf("%s ", tp);
1838         }
1839         printf("\n");
1840     }
1841     return error;
1842 }
1843
1844
1845 static int
1846 DiskFreeCmd(struct cmd_syndesc *as, void *arock)
1847 {
1848     afs_int32 code;
1849     struct ViceIoctl blob;
1850     struct cmd_item *ti;
1851     char *name;
1852     struct VolumeStatus *status;
1853     int error = 0;
1854     
1855     printf("%-25s%-10s%-10s%-10s%-6s\n", "Volume Name", "    kbytes",
1856            "      used", "     avail", " %used");
1857     SetDotDefault(&as->parms[0].items);
1858     for(ti=as->parms[0].items; ti; ti=ti->next) {
1859         /* once per file */
1860         blob.out_size = AFS_PIOCTL_MAXSIZE;
1861         blob.in_size = 0;
1862         blob.out = space;
1863         code = pioctl_utf8(ti->data, VIOCGETVOLSTAT, &blob, 1);
1864         if (code) {
1865             Die(errno, ti->data);
1866             error = 1;
1867             continue;
1868         }
1869         space[blob.out_size - 1] = '\0';
1870         status = (VolumeStatus *)space;
1871         name = (char *)status + sizeof(*status);
1872         QuickPrintSpace(status, name);
1873     }
1874     return error;
1875 }
1876
1877 static int
1878 QuotaCmd(struct cmd_syndesc *as, void *arock)
1879 {
1880     afs_int32 code;
1881     struct ViceIoctl blob;
1882     struct cmd_item *ti;
1883     double quotaPct;
1884     struct VolumeStatus *status;
1885     int error = 0;
1886     
1887     SetDotDefault(&as->parms[0].items);
1888     for(ti=as->parms[0].items; ti; ti=ti->next) {
1889         /* once per file */
1890         blob.out_size = AFS_PIOCTL_MAXSIZE;
1891         blob.in_size = 0;
1892         blob.out = space;
1893         code = pioctl_utf8(ti->data, VIOCGETVOLSTAT, &blob, 1);
1894         if (code || blob.out_size != sizeof(*status)) {
1895             Die(errno, ti->data);
1896             error = 1;
1897             continue;
1898         }
1899         status = (VolumeStatus *)space;
1900         if (status->MaxQuota) 
1901             quotaPct = ((((double)status->BlocksInUse)/status->MaxQuota) * 100.0);
1902         else 
1903             quotaPct = 0.0;
1904         printf("%2.0f%% of quota used.\n", quotaPct);
1905     }
1906     return error;
1907 }
1908
1909 static int
1910 ListMountCmd(struct cmd_syndesc *as, void *arock)
1911 {
1912     afs_int32 code;
1913     struct ViceIoctl blob;
1914     struct cmd_item *ti;
1915     char orig_name[1024];               /*Original name, may be modified*/
1916     char true_name[1024];               /*``True'' dirname (e.g., symlink target)*/
1917     char parent_dir[1024];              /*Parent directory of true name*/
1918     char *last_component;       /*Last component of true name*/
1919 #ifndef WIN32
1920     struct stat statbuff;               /*Buffer for status info*/
1921 #endif /* not WIN32 */
1922 #ifndef WIN32
1923     int link_chars_read;                /*Num chars read in readlink()*/
1924 #endif /* not WIN32 */
1925     int thru_symlink;                   /*Did we get to a mount point via a symlink?*/
1926     
1927     int error = 0;
1928     for(ti=as->parms[0].items; ti; ti=ti->next) {
1929         /* once per file */
1930         thru_symlink = 0;
1931 #ifdef WIN32
1932         strcpy(orig_name, ti->data);
1933 #else /* not WIN32 */
1934         sprintf(orig_name, "%s%s",
1935                 (ti->data[0] == '/') ? "" : "./",
1936                 ti->data);
1937 #endif /* not WIN32 */
1938
1939 #ifndef WIN32
1940         if (lstat(orig_name, &statbuff) < 0) {
1941             /* if lstat fails, we should still try the pioctl, since it
1942              * may work (for example, lstat will fail, but pioctl will
1943              * work if the volume of offline (returning ENODEV). */
1944             statbuff.st_mode = S_IFDIR; /* lie like pros */
1945         }
1946
1947         /*
1948          * The lstat succeeded.  If the given file is a symlink, substitute
1949          * the file name with the link name.
1950          */
1951         if ((statbuff.st_mode & S_IFMT) == S_IFLNK) {
1952             thru_symlink = 1;
1953             /*
1954              * Read name of resolved file.
1955              */
1956             link_chars_read = readlink(orig_name, true_name, 1024);
1957             if (link_chars_read <= 0) {
1958                 fprintf(stderr,
1959                         "%s: Can't read target name for '%s' symbolic link!\n",
1960                        pn, orig_name);
1961                 error = 1;
1962                 continue;
1963             }
1964
1965             /*
1966              * Add a trailing null to what was read, bump the length.
1967              */
1968             true_name[link_chars_read++] = 0;
1969
1970             /*
1971              * If the symlink is an absolute pathname, we're fine.  Otherwise, we
1972              * have to create a full pathname using the original name and the
1973              * relative symlink name.  Find the rightmost slash in the original
1974              * name (we know there is one) and splice in the symlink value.
1975              */
1976             if (true_name[0] != '\\') {
1977                 last_component = (char *) strrchr(orig_name, '\\');
1978                 strcpy(++last_component, true_name);
1979                 strcpy(true_name, orig_name);
1980             }
1981         } else
1982             strcpy(true_name, orig_name);
1983 #else   /* WIN32 */
1984         strcpy(true_name, orig_name);
1985 #endif /* WIN32 */
1986
1987         /*
1988          * Find rightmost slash, if any.
1989          */
1990 #ifdef WIN32
1991         last_component = (char *) strrchr(true_name, '\\');
1992         if (!last_component)
1993 #endif /* WIN32 */
1994             last_component = (char *) strrchr(true_name, '/');
1995         if (last_component) {
1996             /*
1997              * Found it.  Designate everything before it as the parent directory,
1998              * everything after it as the final component.
1999              */
2000             strncpy(parent_dir, true_name, last_component - true_name + 1);
2001             parent_dir[last_component - true_name + 1] = 0;
2002             last_component++;   /*Skip the slash*/
2003 #ifdef WIN32
2004             if (!InAFS(parent_dir)) {
2005                 const char * nbname = NetbiosName();
2006                 int len = (int)strlen(nbname);
2007
2008                 if (parent_dir[0] == '\\' && parent_dir[1] == '\\' &&
2009                     parent_dir[len+2] == '\\' &&
2010                     parent_dir[len+3] == '\0' &&
2011                     !strnicmp(nbname,&parent_dir[2],len))
2012                 {
2013                     sprintf(parent_dir,"\\\\%s\\all\\", nbname);
2014                 }
2015             }
2016 #endif
2017         } else {
2018             /*
2019              * No slash appears in the given file name.  Set parent_dir to the current
2020              * directory, and the last component as the given name.
2021              */
2022             fs_ExtractDriveLetter(true_name, parent_dir);
2023             strcat(parent_dir, ".");
2024             last_component = true_name;
2025             fs_StripDriveLetter(true_name, true_name, sizeof(true_name));
2026         }
2027
2028         if (strcmp(last_component, ".") == 0 || strcmp(last_component, "..") == 0) {
2029             fprintf(stderr,"%s: you may not use '.' or '..' as the last component\n",pn);
2030             fprintf(stderr,"%s: of a name in the 'fs lsmount' command.\n",pn);
2031             error = 1;
2032             continue;
2033         }
2034
2035         blob.in = last_component;
2036         blob.in_size = (long)strlen(last_component)+1;
2037         blob.out_size = AFS_PIOCTL_MAXSIZE;
2038         blob.out = space;
2039         memset(space, 0, AFS_PIOCTL_MAXSIZE);
2040
2041         code = pioctl_utf8(parent_dir, VIOC_AFS_STAT_MT_PT, &blob, 1);
2042
2043         if (code == 0) {
2044             printf("'%s' is a %smount point for volume '%.*s'\n",
2045                    ti->data,
2046                    (thru_symlink ? "symbolic link, leading to a " : ""),
2047                    blob.out_size,
2048                    space);
2049
2050         } else {
2051             if (errno == EINVAL) {
2052                 fprintf(stderr,"'%s' is not a mount point.\n", ti->data);
2053             } else {
2054                 Die(errno, (ti->data ? ti->data : parent_dir));
2055             }
2056             error = 1;
2057         }
2058     }
2059     return error;
2060 }
2061
2062 static int
2063 MakeMountCmd(struct cmd_syndesc *as, void *arock)
2064 {
2065     afs_int32 code;
2066     char *cellName, *volName, *tmpName;
2067 #ifdef WIN32
2068     char localCellName[128];
2069 #endif
2070     char path[1024] = "";
2071     struct afsconf_cell info;
2072     struct vldbentry vldbEntry;
2073     struct ViceIoctl blob;
2074     char * parent;
2075
2076     memset(&info, 0, sizeof(info));
2077
2078     if (as->parms[2].items)     /* cell name specified */
2079         cellName = as->parms[2].items->data;
2080     else
2081         cellName = NULL;
2082     volName = as->parms[1].items->data;
2083
2084     if (strlen(volName) >= 64) {
2085         fprintf(stderr,"%s: volume name too long (length must be < 64 characters)\n", pn);
2086         return 1;
2087     }
2088
2089     /* Check for a cellname in the volume specification, and complain
2090      * if it doesn't match what was specified with -cell */
2091     if (tmpName = strchr(volName, ':')) {
2092         *tmpName = '\0';
2093         if (cellName) {
2094             if (strcasecmp(cellName,volName)) {
2095                 fprintf(stderr,"fs: cellnames do not match.\n");
2096                 return 1;
2097             }
2098         }
2099         cellName = volName;
2100         volName = ++tmpName;
2101     }
2102
2103     parent = Parent(as->parms[0].items->data);
2104     if (!InAFS(parent)) {
2105 #ifdef WIN32
2106         const char * nbname = NetbiosName();
2107         int len = (int)strlen(nbname);
2108
2109         if (parent[0] == '\\' && parent[1] == '\\' &&
2110             parent[len+2] == '\\' &&
2111             parent[len+3] == '\0' &&
2112             !strnicmp(nbname,&parent[2],len))
2113         {
2114             sprintf(path,"%sall\\%s", parent, &as->parms[0].items->data[strlen(parent)]);
2115             parent = Parent(path);
2116             if (!InAFS(parent)) {
2117                 fprintf(stderr,"%s: mount points must be created within the AFS file system\n", pn);
2118                 return 1;
2119             }
2120         } else 
2121 #endif
2122         {
2123             fprintf(stderr,"%s: mount points must be created within the AFS file system\n", pn);
2124             return 1;
2125         }
2126     }
2127
2128     if ( strlen(path) == 0 )
2129         strcpy(path, as->parms[0].items->data);
2130
2131     if ( IsFreelanceRoot(parent) ) {
2132         if ( !IsAdmin() ) {
2133             fprintf(stderr,"%s: Only AFS Client Administrators may alter the root.afs volume\n", pn);
2134             return 1;
2135         }
2136
2137         if (!cellName) {
2138             blob.in_size = 0;
2139             blob.out_size = sizeof(localCellName);
2140             blob.out = localCellName;
2141             code = pioctl_utf8(parent, VIOC_GET_WS_CELL, &blob, 1);
2142             if (!code) {
2143                 localCellName[sizeof(localCellName) - 1] = '\0';
2144                 cellName = localCellName;
2145             }
2146         }
2147     } else {
2148         if (!cellName) {
2149             code = GetCell(parent,space);
2150             if (code)
2151                 return 1;
2152         }
2153     }
2154
2155     code = GetCellName(cellName?cellName:space, &info);
2156     if (code) {
2157         return 1;
2158     }
2159     if (!(as->parms[4].items)) {
2160       /* not fast, check which cell the mountpoint is being created in */
2161       code = 0;
2162         /* not fast, check name with VLDB */
2163       if (!code)
2164         code = VLDBInit(1, &info);
2165       if (code == 0) {
2166           /* make the check.  Don't complain if there are problems with init */
2167           code = ubik_VL_GetEntryByNameO(uclient, 0, volName, &vldbEntry);
2168           if (code == VL_NOENT) {
2169               fprintf(stderr,"%s: warning, volume %s does not exist in cell %s.\n",
2170                       pn, volName, cellName ? cellName : space);
2171           }
2172       }
2173     }
2174
2175     if (as->parms[3].items)     /* if -rw specified */
2176         strcpy(space, "%");
2177     else
2178         strcpy(space, "#");
2179     if (cellName) {
2180         /* cellular mount point, prepend cell prefix */
2181         strcat(space, info.name);
2182         strcat(space, ":");
2183     }
2184     strcat(space, volName);     /* append volume name */
2185     strcat(space, ".");         /* stupid convention; these end with a period */
2186 #ifdef WIN32
2187     /* create symlink with a special pioctl for Windows NT, since it doesn't
2188      * have a symlink system call.
2189      */
2190     blob.out_size = 0;
2191     blob.in_size = 1 + (long)strlen(space);
2192     blob.in = space;
2193     blob.out = NULL;
2194     code = pioctl_utf8(path, VIOC_AFS_CREATE_MT_PT, &blob, 0);
2195 #else /* not WIN32 */
2196     code = symlink(space, path);
2197 #endif /* not WIN32 */
2198
2199     if (info.linkedCell)
2200         free(info.linkedCell);
2201
2202     if (code) {
2203         Die(errno, path);
2204         return 1;
2205     }
2206     return 0;
2207 }
2208
2209 /*
2210  * Delete AFS mount points.  Variables are used as follows:
2211  *       tbuffer: Set to point to the null-terminated directory name of the mount point
2212  *          (or ``.'' if none is provided)
2213  *      tp: Set to point to the actual name of the mount point to nuke.
2214  */
2215 static int
2216 RemoveMountCmd(struct cmd_syndesc *as, void *arock) {
2217     afs_int32 code=0;
2218     struct ViceIoctl blob;
2219     struct cmd_item *ti;
2220     char tbuffer[1024];
2221     char lsbuffer[1024];
2222     char *tp;
2223     int error = 0;
2224     
2225     for(ti=as->parms[0].items; ti; ti=ti->next) {
2226         /* once per file */
2227         tp = (char *) strrchr(ti->data, '\\');
2228         if (!tp)
2229             tp = (char *) strrchr(ti->data, '/');
2230         if (tp) {
2231             strncpy(tbuffer, ti->data, code=(afs_int32)(tp-ti->data+1));  /* the dir name */
2232             tbuffer[code] = 0;
2233             tp++;   /* skip the slash */
2234
2235 #ifdef WIN32
2236             if (!InAFS(tbuffer)) {
2237                 const char * nbname = NetbiosName();
2238                 int len = (int)strlen(nbname);
2239
2240                 if (tbuffer[0] == '\\' && tbuffer[1] == '\\' &&
2241                     tbuffer[len+2] == '\\' &&
2242                     tbuffer[len+3] == '\0' &&
2243                     !strnicmp(nbname,&tbuffer[2],len))
2244                 {
2245                     sprintf(tbuffer,"\\\\%s\\all\\", nbname);
2246                 }
2247             }
2248 #endif
2249         } else {
2250             fs_ExtractDriveLetter(ti->data, tbuffer);
2251             strcat(tbuffer, ".");
2252             tp = ti->data;
2253             fs_StripDriveLetter(tp, tp, 0);
2254         }
2255         blob.in = tp;
2256         blob.in_size = (long)strlen(tp)+1;
2257         blob.out = lsbuffer;
2258         blob.out_size = sizeof(lsbuffer);
2259         code = pioctl_utf8(tbuffer, VIOC_AFS_STAT_MT_PT, &blob, 0);
2260         if (code) {
2261             if (errno == EINVAL) {
2262                 fprintf(stderr,"%s: '%s' is not a mount point.\n", pn, ti->data);
2263             } else {
2264                 Die(errno, ti->data);
2265             }
2266             error = 1;
2267             continue;   /* don't bother trying */
2268         }
2269
2270         if ( IsFreelanceRoot(tbuffer) && !IsAdmin() ) {
2271             fprintf(stderr,"%s: Only AFS Client Administrators may alter the root.afs volume\n", pn);
2272             error = 1;
2273             continue;   /* skip */
2274         }
2275
2276         blob.out_size = 0;
2277         blob.in = tp;
2278         blob.in_size = (long)strlen(tp)+1;
2279         code = pioctl_utf8(tbuffer, VIOC_AFS_DELETE_MT_PT, &blob, 0);
2280         if (code) {
2281             Die(errno, ti->data);
2282             error = 1;
2283         }
2284     }
2285     return error;
2286 }
2287
2288 /*
2289 */
2290
2291 static int
2292 CheckServersCmd(struct cmd_syndesc *as, void *arock)
2293 {
2294     afs_int32 code;
2295     struct ViceIoctl blob;
2296     afs_int32 j;
2297     afs_int32 temp;
2298     char *tp;
2299     struct afsconf_cell info;
2300     struct chservinfo checkserv;
2301
2302     memset(&info, 0, sizeof(info));
2303     memset(&checkserv, 0, sizeof(struct chservinfo));
2304     blob.in_size=sizeof(struct chservinfo);
2305     blob.in=(caddr_t)&checkserv;
2306
2307     blob.out_size = AFS_PIOCTL_MAXSIZE;
2308     blob.out = space;
2309     memset(space, 0, sizeof(afs_int32));        /* so we assure zero when nothing is copied back */
2310
2311     /* prepare flags for checkservers command */
2312     temp = 2;   /* default to checking local cell only */
2313     if (as->parms[2].items) 
2314         temp |= 1;      /* set fast flag */
2315     if (as->parms[1].items) 
2316         temp &= ~2;     /* turn off local cell check */
2317     
2318     checkserv.magic = 0x12345678;       /* XXX */
2319     checkserv.tflags=temp;
2320
2321     /* now copy in optional cell name, if specified */
2322     if (as->parms[0].items) {
2323         code = GetCellName(as->parms[0].items->data, &info);
2324         if (code) {
2325             return 1;
2326         }
2327         strcpy(checkserv.tbuffer,info.name);
2328         checkserv.tsize=(int)strlen(info.name)+1;
2329         if (info.linkedCell)
2330             free(info.linkedCell);
2331     } else {
2332         strcpy(checkserv.tbuffer,"\0");
2333         checkserv.tsize=0;
2334     }
2335
2336     if(as->parms[3].items) {
2337         checkserv.tinterval=atol(as->parms[3].items->data);
2338
2339         /* sanity check */
2340         if(checkserv.tinterval<0) {
2341             printf("Warning: The negative -interval is ignored; treated as an inquiry\n");
2342             checkserv.tinterval=-1;
2343         } else if(checkserv.tinterval> 600) {
2344             printf("Warning: The maximum -interval value is 10 mins (600 secs)\n");
2345             checkserv.tinterval=600;    /* 10 min max interval */
2346         }       
2347     } else {
2348         checkserv.tinterval = -1;       /* don't change current interval */
2349     }
2350
2351     if ( checkserv.tinterval >= 0 ) {
2352 #ifdef WIN32
2353         if ( !IsAdmin() ) {
2354             fprintf (stderr,"Permission denied: requires AFS Client Administrator access.\n");
2355             return EACCES;
2356         }
2357 #else /* WIN32 */
2358         if (geteuid()) {
2359             fprintf (stderr,"Permission denied: requires root access.\n");
2360             return EACCES;
2361         }
2362 #endif /* WIN32 */
2363     }
2364
2365     code = pioctl_utf8(0, VIOCCKSERV, &blob, 1);
2366     if (code) {
2367         if ((errno == EACCES) && (checkserv.tinterval > 0)) {
2368             printf("Must be root to change -interval\n");
2369             return code;
2370         }
2371         Die(errno, 0);
2372         return 1;
2373     }
2374     memcpy(&temp, space, sizeof(afs_int32));
2375     if (checkserv.tinterval >= 0) {
2376         if (checkserv.tinterval > 0) 
2377             printf("The new down server probe interval (%d secs) is now in effect (old interval was %d secs)\n", 
2378                    checkserv.tinterval, temp);
2379         else 
2380             printf("The current down server probe interval is %d secs\n", temp);
2381         return 0;
2382     }
2383     if (temp == 0) {
2384         printf("All servers are running.\n");
2385     } else {
2386         printf("These servers unavailable due to network or server problems: ");
2387         for(j=0; j < AFS_MAXHOSTS; j++) {
2388             memcpy(&temp, space + j*sizeof(afs_int32), sizeof(afs_int32));
2389             if (temp == 0) 
2390                 break;
2391             tp = hostutil_GetNameByINet(temp);
2392             printf(" %s", tp);
2393         }
2394         printf(".\n");
2395         code = 1;       /* XXX */
2396     }
2397     return code;
2398 }
2399
2400 static int
2401 MessagesCmd(struct cmd_syndesc *as, void *arock)
2402 {
2403     afs_int32 code=0;
2404     struct ViceIoctl blob;
2405     struct gaginfo gagflags;
2406     struct cmd_item *show;
2407     
2408     memset(&gagflags, 0, sizeof(struct gaginfo));
2409     blob.in_size = sizeof(struct gaginfo);
2410     blob.in = (caddr_t ) &gagflags;
2411     blob.out_size = AFS_PIOCTL_MAXSIZE;
2412     blob.out = space;
2413     memset(space, 0, sizeof(afs_int32));        /* so we assure zero when nothing is copied back */
2414
2415     if (show = as->parms[0].items) {
2416         if (!strcasecmp (show->data, "user"))
2417             gagflags.showflags |= GAGUSER;
2418         else if (!strcasecmp (show->data, "console"))
2419             gagflags.showflags |= GAGCONSOLE;
2420         else if (!strcasecmp (show->data, "all"))
2421             gagflags.showflags |= GAGCONSOLE | GAGUSER;
2422         else if (!strcasecmp (show->data, "none"))
2423             /* do nothing */ ;
2424         else {
2425             fprintf(stderr, 
2426                      "unrecognized flag %s: must be in {user,console,all,none}\n", 
2427                      show->data);
2428             code = EINVAL;
2429         }
2430     }
2431  
2432     if (code)
2433         return 1;
2434
2435     code = pioctl_utf8(0, VIOC_GAG, &blob, 1);
2436     if (code) {
2437         Die(errno, 0);
2438         return 1;
2439     }
2440     return 0;
2441 }
2442
2443 static int
2444 CheckVolumesCmd(struct cmd_syndesc *as, void *arock)
2445 {
2446     afs_int32 code;
2447     struct ViceIoctl blob;
2448     
2449     blob.in_size = 0;
2450     blob.out_size = 0;
2451     code = pioctl_utf8(0, VIOCCKBACK, &blob, 1);
2452     if (code) {
2453         Die(errno, 0);
2454         return 1;
2455     }
2456     printf("All volumeID/name mappings checked.\n");
2457     
2458     return 0;
2459 }
2460
2461 static int
2462 SetCacheSizeCmd(struct cmd_syndesc *as, void *arock)
2463 {
2464     afs_int32 code;
2465     struct ViceIoctl blob;
2466     afs_int32 temp;
2467     
2468 #ifdef WIN32
2469     if ( !IsAdmin() ) {
2470         fprintf (stderr,"Permission denied: requires AFS Client Administrator access.\n");
2471         return EACCES;
2472     }
2473 #else /* WIN32 */
2474     if (geteuid()) {
2475         fprintf (stderr,"Permission denied: requires root access.\n");
2476         return EACCES;
2477     }
2478 #endif /* WIN32 */
2479
2480     if (!as->parms[0].items && !as->parms[1].items) {
2481         fprintf(stderr,"%s: syntax error in set cache size cmd.\n", pn);
2482         return 1;
2483     }
2484     if (as->parms[0].items) {
2485         code = util_GetHumanInt32(as->parms[0].items->data, &temp);
2486         if (code) {
2487             fprintf(stderr,"%s: bad integer specified for cache size.\n", pn);
2488             return 1;
2489         }
2490     } else
2491         temp = 0;
2492     blob.in = (char *) &temp;
2493     blob.in_size = sizeof(afs_int32);
2494     blob.out_size = 0;
2495     code = pioctl_utf8(0, VIOCSETCACHESIZE, &blob, 1);
2496     if (code) {
2497         Die(errno, (char *) 0);
2498         return 1;
2499     } 
2500       
2501     printf("New cache size set.\n");
2502     return 0;
2503 }
2504
2505 static int
2506 GetCacheParmsCmd(struct cmd_syndesc *as, void *arock)
2507 {
2508     afs_int32 code;
2509     struct ViceIoctl blob;
2510     cm_cacheParms_t parms;
2511
2512     memset(&parms, 0, sizeof(parms));
2513     blob.in = NULL;
2514     blob.in_size = 0;
2515     blob.out_size = sizeof(parms);
2516     blob.out = (char *) &parms;
2517     code = pioctl_utf8(0, VIOCGETCACHEPARMS, &blob, 1);
2518     if (code || blob.out_size != sizeof(parms)) {
2519         Die(errno, NULL);
2520         return 1;
2521     }
2522      
2523     printf("AFS using %I64u of the cache's available %I64u 1K byte blocks.\n",
2524            parms.parms[1], parms.parms[0]);
2525     if (parms.parms[1] > parms.parms[0])
2526         printf("[Cache guideline temporarily deliberately exceeded; it will be adjusted down but you may wish to increase the cache size.]\n");
2527     return 0;
2528 }
2529
2530 static int
2531 ListCellsCmd(struct cmd_syndesc *as, void *arock)
2532 {
2533     afs_int32 code;
2534     afs_int32 i, j, *lp, magic, size;
2535     char *tp;
2536     afs_int32 addr, maxa = AFS_OMAXHOSTS;
2537     struct ViceIoctl blob;
2538     int resolve;
2539
2540     resolve = !(as->parms[0].items);    /* -numeric */
2541     
2542     for(i=0;i<1000;i++) {
2543         tp = space;
2544         memcpy(tp, &i, sizeof(afs_int32));
2545         tp = (char *)(space + sizeof(afs_int32));
2546         lp = (afs_int32 *)tp;
2547         *lp++ = 0x12345678;
2548         size = sizeof(afs_int32) + sizeof(afs_int32);
2549         blob.out_size = AFS_PIOCTL_MAXSIZE;
2550         blob.in_size = sizeof(afs_int32);
2551         blob.in = space;
2552         blob.out = space;
2553         code = pioctl_utf8(0, VIOCGETCELL, &blob, 1);
2554         if (code < 0) {
2555             if (errno == EDOM) 
2556                 break;  /* done with the list */
2557             Die(errno, 0);
2558             return 1;
2559         }       
2560         tp = space;
2561         memcpy(&magic, tp, sizeof(afs_int32));  
2562         if (magic == 0x12345678) {
2563             maxa = AFS_MAXHOSTS;
2564             tp += sizeof(afs_int32);
2565         }
2566         printf("Cell %s on hosts", tp+maxa*sizeof(afs_int32));
2567         for(j=0; j < maxa && j*sizeof(afs_int32) < AFS_PIOCTL_MAXSIZE; j++) {
2568             char *name, tbuffer[20];
2569
2570             memcpy(&addr, tp + j*sizeof(afs_int32), sizeof(afs_int32));
2571             if (addr == 0) 
2572                 break;
2573
2574             if (resolve) {
2575                 name = hostutil_GetNameByINet(addr);
2576             } else {
2577                 addr = ntohl(addr);
2578                 sprintf(tbuffer, "%d.%d.%d.%d", (addr >> 24) & 0xff,
2579                          (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff);
2580                 name = tbuffer;
2581             }
2582             printf(" %s", name);
2583         }
2584         printf(".\n");
2585     }
2586     return 0;
2587 }
2588
2589 #ifndef WIN32
2590 static int
2591 ListAliasesCmd(struct cmd_syndesc *as, void *arock)
2592 {
2593     afs_int32 code, i;
2594     char *tp, *aliasName, *realName;
2595     struct ViceIoctl blob;
2596
2597     for (i = 0;; i++) {
2598         tp = space;
2599         memcpy(tp, &i, sizeof(afs_int32));
2600         blob.out_size = AFS_PIOCTL_MAXSIZE;
2601         blob.in_size = sizeof(afs_int32);
2602         blob.in = space;
2603         blob.out = space;
2604         code = pioctl_utf8(0, VIOC_GETALIAS, &blob, 1);
2605         if (code < 0) {
2606             if (errno == EDOM)
2607                 break;          /* done with the list */
2608             Die(errno, 0);
2609             return 1;
2610         }
2611         space[blob.out_size - 1] = '\0';
2612         tp = space;
2613         aliasName = tp;
2614         tp += strlen(aliasName) + 1;
2615         realName = tp;
2616         printf("Alias %s for cell %s\n", aliasName, realName);
2617     }
2618     return 0;
2619 }
2620
2621 static int
2622 CallBackRxConnCmd(struct cmd_syndesc *as, void *arock)
2623 {
2624     afs_int32 code;
2625     struct ViceIoctl blob;
2626     struct cmd_item *ti;
2627     afs_int32 hostAddr;
2628     struct hostent *thp;
2629     char *tp;
2630     int setp;
2631     
2632     ti = as->parms[0].items;
2633     setp = 1;
2634     if (ti) {
2635         thp = hostutil_GetHostByName(ti->data);
2636         if (!thp) {
2637             fprintf(stderr, "host %s not found in host table.\n", ti->data);
2638             return 1;
2639         }
2640         else memcpy(&hostAddr, thp->h_addr, sizeof(afs_int32));
2641     } else {
2642         hostAddr = 0;   /* means don't set host */
2643         setp = 0;       /* aren't setting host */
2644     }
2645     
2646     /* now do operation */
2647     blob.in_size = sizeof(afs_int32);
2648     blob.out_size = sizeof(afs_int32);
2649     blob.in = (char *) &hostAddr;
2650     blob.out = (char *) &hostAddr;
2651     
2652     code = pioctl_utf8(0, VIOC_CBADDR, &blob, 1);
2653     if (code < 0) {
2654         Die(errno, 0);
2655         return 1;
2656     }
2657     return 0;
2658 }
2659 #endif /* WIN32 */
2660
2661 static int
2662 NewCellCmd(struct cmd_syndesc *as, void *arock)
2663 {
2664 #ifndef WIN32
2665     afs_int32 code, linkedstate=0, size=0, *lp;
2666     struct ViceIoctl blob;
2667     struct cmd_item *ti;
2668     char *tp, *cellname=0;
2669     struct hostent *thp;
2670     afs_int32 fsport = 0, vlport = 0;
2671
2672     memset(space, 0, AFS_MAXHOSTS * sizeof(afs_int32));
2673     tp = space;
2674     lp = (afs_int32 *)tp;
2675     *lp++ = 0x12345678;
2676     tp += sizeof(afs_int32);
2677     for(ti=as->parms[1].items; ti; ti=ti->next) {
2678         thp = hostutil_GetHostByName(ti->data);
2679         if (!thp) {
2680             fprintf(stderr,"%s: Host %s not found in host table, skipping it.\n",
2681                    pn, ti->data);
2682         }
2683         else {
2684             memcpy(tp, thp->h_addr, sizeof(afs_int32));
2685             tp += sizeof(afs_int32);
2686         }
2687     }
2688     if (as->parms[2].items) {
2689         /*
2690          * Link the cell, for the purposes of volume location, to the specified
2691          * cell.
2692          */
2693         cellname = as->parms[2].items->data;
2694         linkedstate = 1;
2695     }
2696 #ifdef FS_ENABLE_SERVER_DEBUG_PORTS
2697     if (as->parms[3].items) {
2698         code = util_GetInt32(as->parms[3].items->data, &vlport);
2699         if (code) {
2700             fprintf(stderr,"fs: bad integer specified for the fileserver port.\n");
2701             return code;
2702         }
2703     }
2704     if (as->parms[4].items) {
2705         code = util_GetInt32(as->parms[4].items->data, &fsport);
2706         if (code) {
2707             fprintf(stderr,"fs: bad integer specified for the vldb server port.\n");
2708             return code;
2709         }
2710     }
2711 #endif
2712     tp = (char *)(space + (AFS_MAXHOSTS+1) *sizeof(afs_int32));
2713     lp = (afs_int32 *)tp;    
2714     *lp++ = fsport;
2715     *lp++ = vlport;
2716     *lp = linkedstate;
2717     strcpy(space +  ((AFS_MAXHOSTS+4) * sizeof(afs_int32)), as->parms[0].items->data);
2718     size = ((AFS_MAXHOSTS+4) * sizeof(afs_int32)) + strlen(as->parms[0].items->data) + 1 /* for null */;
2719     tp = (char *)(space + size);
2720     if (linkedstate) {
2721         strcpy(tp, cellname);
2722         size += strlen(cellname) + 1;
2723     }
2724     blob.in_size = size;
2725     blob.in = space;
2726     blob.out_size = 0;
2727     code = pioctl_utf8(0, VIOCNEWCELL, &blob, 1);
2728     if (code < 0)
2729         Die(errno, 0);
2730     return 0;
2731 #else /* WIN32 */
2732     afs_int32 code;
2733     struct ViceIoctl blob;
2734     
2735     if ( !IsAdmin() ) {
2736         fprintf (stderr,"Permission denied: requires AFS Client Administrator access.\n");
2737         return EACCES;
2738     }
2739
2740     blob.in_size = 0;
2741     blob.in = (char *) 0;
2742     blob.out_size = AFS_PIOCTL_MAXSIZE;
2743     blob.out = space;
2744
2745     code = pioctl_utf8((char *) 0, VIOCNEWCELL, &blob, 1);
2746
2747     if (code) {
2748         Die(errno, (char *) 0);
2749         return 1;
2750     }
2751     
2752     printf("Cell servers information refreshed\n");
2753     return 0;
2754 #endif /* WIN32 */
2755 }
2756
2757 #ifndef WIN32
2758 static int
2759 NewAliasCmd(struct cmd_syndesc *as, void *arock)
2760 {
2761     afs_int32 code;
2762     struct ViceIoctl blob;
2763     char *tp;
2764     char *aliasName, *realName;
2765
2766     /* Setup and do the NEWALIAS pioctl call */
2767     aliasName = as->parms[0].items->data;
2768     realName = as->parms[1].items->data;
2769     tp = space;
2770     strcpy(tp, aliasName);
2771     tp += strlen(aliasName) + 1;
2772     strcpy(tp, realName);
2773     tp += strlen(realName) + 1;
2774
2775     blob.in_size = tp - space;
2776     blob.in = space;
2777     blob.out_size = 0;
2778     blob.out = space;
2779     code = pioctl_utf8(0, VIOC_NEWALIAS, &blob, 1);
2780     if (code < 0) {
2781         if (errno == EEXIST) {
2782             fprintf(stderr,
2783                     "%s: cell name `%s' in use by an existing cell.\n", pn,
2784                     aliasName);
2785         } else {
2786             Die(errno, 0);
2787         }
2788         return 1;
2789     }
2790     return 0;
2791 }
2792 #endif /* WIN32 */
2793
2794 static int
2795 WhichCellCmd(struct cmd_syndesc *as, void *arock)
2796 {
2797     afs_int32 code;
2798     struct cmd_item *ti;
2799     struct ViceIoctl blob;
2800     int error = 0;
2801     int literal = 0;
2802     cm_ioctlQueryOptions_t options;
2803
2804     if (as->parms[1].items)
2805         literal = 1;
2806     
2807     SetDotDefault(&as->parms[0].items);
2808     for(ti=as->parms[0].items; ti; ti=ti->next) {
2809         cm_fid_t fid;
2810         afs_uint32 filetype;
2811         char cell[CELL_MAXNAMELEN];
2812
2813         /* once per file */
2814         memset(&fid, 0, sizeof(fid));
2815         memset(&options, 0, sizeof(options));
2816         filetype = 0;
2817         options.size = sizeof(options);
2818         options.field_flags |= CM_IOCTL_QOPTS_FIELD_LITERAL;
2819         options.literal = literal;
2820         blob.in_size = options.size;    /* no variable length data */
2821         blob.in = &options;
2822
2823         blob.out_size = sizeof(cm_fid_t);
2824         blob.out = (char *) &fid;
2825         if (0 == pioctl_utf8(ti->data, VIOCGETFID, &blob, 1) &&
2826             blob.out_size == sizeof(cm_fid_t)) {
2827             options.field_flags |= CM_IOCTL_QOPTS_FIELD_FID;
2828             options.fid = fid;
2829         } else {
2830             Die(errno, ti->data);
2831             error = 1;
2832             continue;
2833         }
2834
2835         blob.out_size = sizeof(filetype);
2836         blob.out = &filetype;
2837
2838         code = pioctl_utf8(ti->data, VIOC_GETFILETYPE, &blob, 1);
2839         if (code || blob.out_size != sizeof(filetype)) {
2840             Die(errno, ti->data);
2841             error = 1;
2842             continue;
2843         }
2844         blob.out_size = CELL_MAXNAMELEN;
2845         blob.out = cell;
2846
2847         code = pioctl_utf8(ti->data, VIOC_FILE_CELL_NAME, &blob, 1);
2848         if (code) {
2849             if (errno == ENOENT)
2850                 fprintf(stderr,"%s: no such cell as '%s'\n", pn, ti->data);
2851             else
2852                 Die(errno, ti->data);
2853             error = 1;
2854             continue;
2855         }
2856         cell[CELL_MAXNAMELEN - 1] = '\0';
2857         printf("%s %s lives in cell '%s'\n",
2858                 filetypestr(filetype),
2859                 ti->data, cell);
2860     }
2861     return error;
2862 }
2863
2864 static int
2865 WSCellCmd(struct cmd_syndesc *as, void *arock)
2866 {
2867     afs_int32 code;
2868     struct ViceIoctl blob;
2869     
2870     blob.in_size = 0;
2871     blob.in = NULL;
2872     blob.out_size = AFS_PIOCTL_MAXSIZE;
2873     blob.out = space;
2874
2875     code = pioctl_utf8(NULL, VIOC_GET_WS_CELL, &blob, 1);
2876
2877     if (code) {
2878         Die(errno, NULL);
2879         return 1;
2880     }
2881     space[AFS_PIOCTL_MAXSIZE - 1] = '\0';
2882     printf("This workstation belongs to cell '%s'\n", space);
2883     return 0;
2884 }
2885
2886 /*
2887 static int
2888 PrimaryCellCmd(struct cmd_syndesc *as, void *arock)
2889 {
2890     fprintf(stderr,"This command is obsolete, as is the concept of a primary token.\n");
2891     return 0;
2892 }
2893 */
2894
2895 #ifndef AFS_NT40_ENV
2896 static int
2897 MonitorCmd(struct cmd_syndesc *as, void *arock)
2898 {
2899     afs_int32 code;
2900     struct ViceIoctl blob;
2901     struct cmd_item *ti;
2902     afs_int32 hostAddr;
2903     struct hostent *thp;
2904     char *tp;
2905     int setp;
2906     
2907     ti = as->parms[0].items;
2908     setp = 1;
2909     if (ti) {
2910         /* set the host */
2911         if (!strcmp(ti->data, "off")) {
2912             hostAddr = 0xffffffff;
2913         } else {
2914             thp = hostutil_GetHostByName(ti->data);
2915             if (!thp) {
2916                 if (!strcmp(ti->data, "localhost")) {
2917                     fprintf(stderr,"localhost not in host table, assuming 127.0.0.1\n");
2918                     hostAddr = htonl(0x7f000001);
2919                 } else {
2920                     fprintf(stderr,"host %s not found in host table.\n", ti->data);
2921                     return 1;
2922                 }
2923             } else {
2924                 memcpy(&hostAddr, thp->h_addr, sizeof(afs_int32));
2925             }
2926         }
2927     } else {
2928         hostAddr = 0;   /* means don't set host */
2929         setp = 0;       /* aren't setting host */
2930     }
2931
2932     /* now do operation */
2933     blob.in_size = sizeof(afs_int32);
2934     blob.out_size = sizeof(afs_int32);
2935     blob.in = (char *) &hostAddr;
2936     blob.out = (char *) &hostAddr;
2937     code = pioctl_utf8(0, VIOC_AFS_MARINER_HOST, &blob, 1);
2938     if (code || blob.out_size != sizeof(afs_int32)) {
2939         Die(errno, 0);
2940         return 1;
2941     }
2942     if (setp) {
2943         printf("%s: new monitor host set.\n", pn);
2944     } else {
2945         /* now decode old address */
2946         if (hostAddr == 0xffffffff) {
2947             printf("Cache monitoring is currently disabled.\n");
2948         } else {
2949             tp = hostutil_GetNameByINet(hostAddr);
2950             printf("Using host %s for monitor services.\n", tp);
2951         }
2952     }
2953     return 0;
2954 }
2955 #endif /* AFS_NT40_ENV */
2956
2957 static int
2958 SysNameCmd(struct cmd_syndesc *as, void *arock)
2959 {
2960     afs_int32 code;
2961     struct ViceIoctl blob;
2962     struct cmd_item *ti;
2963     char *input = space;
2964     afs_int32 setp = 0;
2965     
2966     ti = as->parms[0].items;
2967     if (ti) {
2968 #ifdef WIN32
2969     if ( !IsAdmin() ) {
2970         fprintf (stderr,"Permission denied: requires AFS Client Administrator access.\n");
2971         return EACCES;
2972     }
2973 #else /* WIN32 */
2974     if (geteuid()) {
2975         fprintf (stderr,"Permission denied: requires root access.\n");
2976         return EACCES;
2977     }
2978 #endif /* WIN32 */
2979     }
2980
2981     blob.in = space;
2982     blob.out = space;
2983     blob.out_size = AFS_PIOCTL_MAXSIZE;
2984     blob.in_size = sizeof(afs_int32);
2985     memcpy(input, &setp, sizeof(afs_int32));
2986     input += sizeof(afs_int32);
2987     for (; ti; ti = ti->next) {
2988         setp++;
2989         blob.in_size += (long)strlen(ti->data) + 1;
2990         if (blob.in_size > AFS_PIOCTL_MAXSIZE) {
2991             fprintf(stderr, "%s: sysname%s too long.\n", pn,
2992                      setp > 1 ? "s" : "");
2993             return 1;
2994         }
2995         strcpy(input, ti->data);
2996         input += strlen(ti->data);
2997         *(input++) = '\0';
2998     }
2999     memcpy(space, &setp, sizeof(afs_int32));
3000     code = pioctl_utf8(0, VIOC_AFS_SYSNAME, &blob, 1);
3001     if (code) {
3002         Die(errno, 0);
3003         return 1;
3004     }    
3005     if (setp) {
3006         printf("%s: new sysname%s set.\n", pn, setp > 1 ? " list" : "");
3007         return 0;
3008     }
3009
3010     input = space;
3011     memcpy(&setp, input, sizeof(afs_int32));
3012     input += sizeof(afs_int32);
3013     if (!setp) {
3014         fprintf(stderr,"No sysname name value was found\n");
3015         return 1;
3016     } 
3017     space[blob.out_size - 1] = '\0';
3018     printf("Current sysname%s is", setp > 1 ? " list" : "");
3019     for (; setp > 0; --setp ) {
3020         printf(" \'%s\'", input);
3021         input += strlen(input) + 1;
3022     }
3023     printf("\n");
3024     return 0;
3025 }
3026
3027 #ifndef AFS_NT40_ENV
3028 static char *exported_types[] = {"null", "nfs", ""};
3029 static int ExportAfsCmd(struct cmd_syndesc *as, void *arock)
3030 {
3031     afs_int32 code;
3032     struct ViceIoctl blob;
3033     struct cmd_item *ti;
3034     int export = 0, type = 0, mode = 0, exp = 0, gstat = 0;
3035     int exportcall, pwsync = 0, smounts = 0;
3036     
3037 #ifdef WIN32
3038     if ( !IsAdmin() ) {
3039         fprintf (stderr,"Permission denied: requires AFS Client Administrator access.\n");
3040         return EACCES;
3041     }
3042 #else /* WIN32 */
3043     if (geteuid()) {
3044         fprintf (stderr,"Permission denied: requires root access.\n");
3045         return EACCES;
3046     }
3047 #endif /* WIN32 */
3048
3049     ti = as->parms[0].items;
3050     if (strcmp(ti->data, "nfs") == 0) 
3051         type = 0x71; /* NFS */
3052     else {
3053         fprintf(stderr,
3054                 "Invalid exporter type, '%s', Only the 'nfs' exporter is currently supported\n", ti->data);
3055         return 1;
3056     }
3057     ti = as->parms[1].items;
3058     if (ti) {
3059         if (strcmp(ti->data, "on") == 0) 
3060             export = 3;
3061         else if (strcmp(ti->data, "off") == 0) 
3062             export = 2;
3063         else {
3064             fprintf(stderr, "Illegal argument %s\n", ti->data);
3065             return 1;
3066         }
3067         exp = 1;
3068     }
3069     if (ti = as->parms[2].items) {      /* -noconvert */
3070         if (strcmp(ti->data, "on") == 0) 
3071             mode = 2;
3072         else if (strcmp(ti->data, "off") == 0) 
3073             mode = 3;
3074         else {
3075             fprintf(stderr, "Illegal argument %s\n", ti->data);
3076             return 1;
3077         }
3078     }
3079     if (ti = as->parms[3].items) {      /* -uidcheck */
3080         if (strcmp(ti->data, "on") == 0) 
3081             pwsync = 3;
3082         else if (strcmp(ti->data, "off") == 0) 
3083             pwsync = 2;
3084         else {
3085             fprintf(stderr, "Illegal argument %s\n", ti->data);
3086             return 1;
3087         }
3088     }
3089     if (ti = as->parms[4].items) {      /* -submounts */
3090         if (strcmp(ti->data, "on") == 0) 
3091             smounts = 3;
3092         else if (strcmp(ti->data, "off") == 0) 
3093             smounts = 2;
3094         else {
3095             fprintf(stderr, "Illegal argument %s\n", ti->data);
3096             return 1;
3097         }
3098     }
3099     exportcall =  (type << 24) | (mode << 6) | (pwsync << 4) | (smounts << 2) | export;
3100     type &= ~0x70;
3101     /* make the call */
3102     blob.in = (char *) &exportcall;
3103     blob.in_size = sizeof(afs_int32);
3104     blob.out = (char *) &exportcall;
3105     blob.out_size = sizeof(afs_int32);
3106     code = pioctl_utf8(0, VIOC_EXPORTAFS, &blob, 1);
3107     if (code) {
3108         if (errno == ENODEV) {
3109             fprintf(stderr,
3110                     "Sorry, the %s-exporter type is currently not supported on this AFS client\n", exported_types[type]);
3111         } else {
3112             Die(errno, 0);
3113         }
3114         return 1;
3115     } else {
3116         if (!gstat) {
3117             if (exportcall & 1) {
3118                 printf("'%s' translator is enabled with the following options:\n\tRunning in %s mode\n\tRunning in %s mode\n\t%s\n", 
3119                        exported_types[type], (exportcall & 2 ? "strict unix" : "convert owner mode bits to world/other"),
3120                        (exportcall & 4 ? "strict 'passwd sync'" : "no 'passwd sync'"),
3121                        (exportcall & 8 ? "Allow mounts of /afs/.. subdirs" : "Only mounts to /afs allowed"));
3122             } else {
3123                 printf("'%s' translator is disabled\n", exported_types[type]);
3124             }
3125         }
3126     }
3127     return 0;
3128 }
3129 #endif
3130
3131 static int
3132 GetCellCmd(struct cmd_syndesc *as, void *arock)
3133 {
3134     afs_int32 code;
3135     struct ViceIoctl blob;
3136     struct afsconf_cell info;
3137     struct cmd_item *ti;
3138     struct a {
3139         afs_int32 stat;
3140         afs_int32 junk;
3141     } args;
3142     int error = 0;
3143
3144     memset(&info, 0, sizeof(info));
3145     memset(&args, 0, sizeof(args));      /* avoid Purify UMR error */
3146     for(ti=as->parms[0].items; ti; ti=ti->next) {
3147         /* once per cell */
3148         blob.out_size = sizeof(args);
3149         blob.out = (caddr_t) &args;
3150         code = GetCellName(ti->data, &info);
3151         if (code) {
3152             error = 1;
3153             continue;
3154         }
3155         if (info.linkedCell)
3156             free(info.linkedCell);
3157         blob.in_size = 1+(long)strlen(info.name);
3158         blob.in = info.name;
3159         code = pioctl_utf8(0, VIOC_GETCELLSTATUS, &blob, 1);
3160         if (code) {
3161             if (errno == ENOENT)
3162                 fprintf(stderr,"%s: the cell named '%s' does not exist\n", pn, info.name);
3163             else
3164                 Die(errno, info.name);
3165             error = 1;
3166             continue;
3167         }
3168         printf("Cell %s status: ", info.name);
3169 #ifdef notdef
3170         if (args.stat & 1) 
3171             printf("primary ");
3172 #endif
3173         if (args.stat & 2) 
3174             printf("no setuid allowed");
3175         else 
3176             printf("setuid allowed");
3177         if (args.stat & 4) 
3178             printf(", using old VLDB");
3179         printf("\n");
3180     }
3181     return error;
3182 }
3183
3184 static int SetCellCmd(struct cmd_syndesc *as, void *arock)
3185 {
3186     afs_int32 code;
3187     struct ViceIoctl blob;
3188     struct afsconf_cell info;
3189     struct cmd_item *ti;
3190     struct a {
3191         afs_int32 stat;
3192         afs_int32 junk;
3193         char cname[64];
3194     } args;
3195     int error = 0;
3196
3197     memset(&info, 0, sizeof(info));
3198
3199     /* Check arguments. */
3200     if (as->parms[1].items && as->parms[2].items) {
3201         fprintf(stderr, "Cannot specify both -suid and -nosuid.\n");
3202         return 1;
3203     }
3204
3205     /* figure stuff to set */
3206     args.stat = 0;
3207     args.junk = 0;
3208
3209 #ifdef WIN32
3210     if ( !IsAdmin() ) {
3211         fprintf (stderr,"Permission denied: requires AFS Client Administrator access.\n");
3212         return EACCES;
3213     }
3214 #else /* WIN32 */
3215     if (geteuid()) {
3216         fprintf (stderr,"Permission denied: requires root access.\n");
3217         return EACCES;
3218     }
3219 #endif /* WIN32 */
3220
3221     if (! as->parms[1].items) 
3222         args.stat |= CM_SETCELLFLAG_SUID; /* default to -nosuid */
3223
3224     /* set stat for all listed cells */
3225     for(ti=as->parms[0].items; ti; ti=ti->next) {
3226         /* once per cell */
3227         code = GetCellName(ti->data, &info);
3228         if (code) {
3229             error = 1;
3230             continue;
3231         }
3232         if (info.linkedCell)
3233             free(info.linkedCell);
3234         strcpy(args.cname, info.name);
3235         blob.in_size = sizeof(args);
3236         blob.in = (caddr_t) &args;
3237         blob.out_size = 0;
3238         blob.out = (caddr_t) 0;
3239         code = pioctl_utf8(0, VIOC_SETCELLSTATUS, &blob, 1);
3240         if (code) {
3241             Die(errno, info.name);      /* XXX added cell name to Die() call */
3242             error = 1;
3243         }
3244     }
3245     return error;
3246 }
3247
3248 static int
3249 GetCellName(char *cellNamep, struct afsconf_cell *infop)
3250 {
3251     strcpy(infop->name, cellNamep);
3252     return 0;
3253 }
3254
3255 static int
3256 VLDBInit(int noAuthFlag, struct afsconf_cell *info)
3257 {
3258     afs_int32 code;
3259     char confDir[257];
3260
3261     cm_GetConfigDir(confDir, sizeof(confDir));
3262
3263     code = ugen_ClientInit(noAuthFlag, confDir, 
3264                            info->name, 0, &uclient, 
3265                            NULL, pn, rxkad_clear,
3266                            VLDB_MAXSERVERS, AFSCONF_VLDBSERVICE, 50,
3267                            0, 0, USER_SERVICE_ID);
3268     rxInitDone = 1;
3269     return code;
3270 }
3271
3272 static struct ViceIoctl gblob;
3273 static int debug = 0;
3274 /* 
3275  * here follow some routines in suport of the setserverprefs and
3276  * getserverprefs commands.  They are:
3277  * SetPrefCmd  "top-level" routine
3278  * addServer   adds a server to the list of servers to be poked into the
3279  *             kernel.  Will poke the list into the kernel if it threatens
3280  *             to get too large.
3281  * pokeServers pokes the existing list of servers and ranks into the kernel
3282  * GetPrefCmd  reads the Cache Manager's current list of server ranks
3283  */
3284
3285 #ifdef WIN32
3286 static int 
3287 pokeServers(void)
3288 {
3289     int code;
3290     cm_SSetPref_t *ssp;
3291     code = pioctl_utf8(0, VIOC_SETSPREFS, &gblob, 1);
3292
3293     ssp = (cm_SSetPref_t *)space;
3294     gblob.in_size = (long)(((char *)&(ssp->servers[0])) - (char *)ssp);
3295     gblob.in = space;
3296     return code;
3297 }
3298 #else
3299 /*
3300  * returns -1 if error message printed,
3301  * 0 on success,
3302  * errno value if error and no error message printed
3303  */
3304 static int
3305 pokeServers(void)
3306 {
3307     int code;
3308
3309     code = pioctl_utf8(0, VIOC_SETSPREFS, &gblob, 1);
3310     if (code && (errno == EINVAL)) {
3311         struct setspref *ssp;
3312         ssp = (struct setspref *)gblob.in;
3313         if (!(ssp->flags & DBservers)) {
3314             gblob.in = (void *)&(ssp->servers[0]);
3315             gblob.in_size -= ((char *)&(ssp->servers[0])) - (char *)ssp;
3316             code = pioctl_utf8(0, VIOC_SETSPREFS33, &gblob, 1);
3317             return code ? errno : 0;
3318         }
3319         fprintf(stderr,
3320                 "This cache manager does not support VL server preferences.\n");
3321         return -1;
3322     }
3323
3324     return code ? errno : 0;
3325 }
3326 #endif /* WIN32 */
3327
3328 #ifdef WIN32
3329 static int
3330 addServer(char *name, unsigned short rank)
3331 {  
3332     int code;
3333     cm_SSetPref_t *ssp;
3334     cm_SPref_t *sp;
3335     struct hostent *thostent;
3336
3337 #ifndef MAXUSHORT
3338 #ifdef MAXSHORT
3339 #define MAXUSHORT ((unsigned short) 2*MAXSHORT+1)  /* assumes two's complement binary system */
3340 #else
3341 #define MAXUSHORT ((unsigned short) ~0)
3342 #endif
3343 #endif
3344
3345     code = 0;
3346     thostent = hostutil_GetHostByName(name);
3347     if (!thostent) {
3348         fprintf (stderr, "%s: couldn't resolve name.\n", name);
3349         return EINVAL;
3350     }
3351
3352     ssp = (cm_SSetPref_t *)(gblob.in);
3353
3354     if (gblob.in_size > MAXINSIZE - sizeof(cm_SPref_t)) {
3355         code = pokeServers();
3356         ssp->num_servers = 0;
3357     }
3358
3359     sp = (cm_SPref_t *)((char*)gblob.in + gblob.in_size);
3360     memcpy (&(sp->host.s_addr), thostent->h_addr, sizeof(afs_uint32));
3361     sp->rank = (rank > MAXUSHORT ? MAXUSHORT : rank);
3362     gblob.in_size += sizeof(cm_SPref_t);
3363     ssp->num_servers++;
3364
3365     if (debug) fprintf(stderr, "adding server %s, rank %d, ip addr 0x%lx\n",name,sp->rank,sp->host.s_addr);
3366
3367     return code;
3368 }
3369 #else
3370 /*
3371  * returns -1 if error message printed,
3372  * 0 on success,
3373  * errno value if error and no error message printed
3374  */
3375 static int
3376 addServer(char *name, afs_int32 rank)
3377 {
3378     int t, code;
3379     struct setspref *ssp;
3380     struct spref *sp;
3381     struct hostent *thostent;
3382     afs_uint32 addr;
3383     int error = 0;
3384
3385 #ifndef MAXUSHORT
3386 #ifdef MAXSHORT
3387 #define MAXUSHORT ((unsigned short) 2*MAXSHORT+1)       /* assumes two's complement binary system */
3388 #else
3389 #define MAXUSHORT ((unsigned short) ~0)
3390 #endif
3391 #endif
3392
3393     thostent = hostutil_GetHostByName(name);
3394     if (!thostent) {
3395         fprintf(stderr, "%s: couldn't resolve name.\n", name);
3396         return -1;
3397     }
3398
3399     ssp = (struct setspref *)(gblob.in);
3400
3401     for (t = 0; thostent->h_addr_list[t]; t++) {
3402         if (gblob.in_size > MAXINSIZE - sizeof(struct spref)) {
3403             code = pokeServers();
3404             if (code)
3405                 error = code;
3406             ssp->num_servers = 0;
3407         }
3408
3409         sp = (struct spref *)(gblob.in + gblob.in_size);
3410         memcpy(&(sp->server.s_addr), thostent->h_addr_list[t],
3411                sizeof(afs_uint32));
3412         sp->rank = (rank > MAXUSHORT ? MAXUSHORT : rank);
3413         gblob.in_size += sizeof(struct spref);
3414         ssp->num_servers++;
3415
3416         if (debug)
3417             fprintf(stderr, "adding server %s, rank %d, ip addr 0x%lx\n",
3418                     name, sp->rank, sp->server.s_addr);
3419     }
3420
3421     return error;
3422 }
3423 #endif /* WIN32 */
3424
3425 #ifdef WIN32
3426 static BOOL IsWindowsNT (void)
3427 {
3428     static BOOL fChecked = FALSE;
3429     static BOOL fIsWinNT = FALSE;
3430
3431     if (!fChecked)
3432     {
3433         OSVERSIONINFO Version;
3434
3435         fChecked = TRUE;
3436
3437         memset (&Version, 0x00, sizeof(Version));
3438         Version.dwOSVersionInfoSize = sizeof(Version);
3439
3440         if (GetVersionEx (&Version))
3441         {
3442             if (Version.dwPlatformId == VER_PLATFORM_WIN32_NT)
3443                 fIsWinNT = TRUE;
3444         }
3445     }
3446     return fIsWinNT;
3447 }
3448 #endif /* WIN32 */
3449
3450 #ifdef WIN32
3451 static int
3452 SetPrefCmd(struct cmd_syndesc *as, void * arock)
3453 {
3454     FILE *infd;
3455     afs_int32 code;
3456     struct cmd_item *ti;
3457     char name[80];
3458     afs_int32 rank;
3459     cm_SSetPref_t *ssp;
3460     
3461     ssp = (cm_SSetPref_t *)space;
3462     ssp->flags = 0;
3463     ssp->num_servers = 0;
3464     gblob.in_size = (long)(((char*)&(ssp->servers[0])) - (char *)ssp);
3465     gblob.in = space;
3466     gblob.out = space;
3467     gblob.out_size = AFS_PIOCTL_MAXSIZE;
3468
3469     if ( !IsAdmin() ) {
3470         fprintf (stderr,"Permission denied: requires AFS Client Administrator access.\n");
3471         return EACCES;
3472     }
3473
3474     code = 0;
3475
3476     ti = as->parms[2].items;  /* -file */
3477     if (ti) {
3478         if (debug) fprintf(stderr,"opening file %s\n",ti->data);
3479         if (!(infd = fopen(ti->data,"r" ))) {
3480             code = errno;
3481             Die(errno,ti->data);
3482         }
3483         else
3484             while ( fscanf(infd, "%79s%ld", name, &rank) != EOF) {
3485                 code = addServer (name, (unsigned short) rank);
3486             }
3487     }
3488
3489     ti = as->parms[3].items;  /* -stdin */
3490     if (ti) {
3491         while ( scanf("%79s%ld", name, &rank) != EOF) {
3492             code = addServer (name, (unsigned short) rank);
3493         }
3494     }
3495
3496     for (ti = as->parms[0].items;ti;ti=ti->next) {/*list of servers, ranks */
3497         if (ti) {
3498             if (!ti->next) {
3499                 break;
3500             }
3501             code = addServer (ti->data, (unsigned short) atol(ti->next->data));
3502             if (debug)
3503                 printf("set fs prefs %s %s\n", ti->data, ti->next->data);
3504             ti=ti->next;
3505         }
3506     }
3507     code = pokeServers();
3508     if (debug) 
3509         printf("now working on vlservers, code=%d, errno=%d\n",code,errno);
3510
3511     ssp = (cm_SSetPref_t *)space;
3512     gblob.in_size = (long)(((char*)&(ssp->servers[0])) - (char *)ssp);
3513     gblob.in = space;
3514     ssp->flags = CM_SPREF_VLONLY;
3515     ssp->num_servers = 0;
3516
3517     for (ti = as->parms[1].items;ti;ti=ti->next) { /* list of dbservers, ranks */
3518         if (ti) {
3519             if (!ti->next) {
3520                 break;
3521             }
3522             code = addServer (ti->data, (unsigned short) atol(ti->next->data));
3523             if (debug) 
3524                 printf("set vl prefs %s %s\n", ti->data, ti->next->data);
3525             ti=ti->next;
3526         }
3527     }
3528
3529     if (as->parms[1].items) {
3530         if (debug) 
3531             printf("now poking vlservers\n");
3532         code = pokeServers();
3533     }
3534
3535     if (code) 
3536         Die(errno,0);
3537
3538     return code;
3539 }
3540 #else
3541 static int
3542 SetPrefCmd(struct cmd_syndesc *as, void *arock)
3543 {
3544     FILE *infd;
3545     afs_int32 code;
3546     struct cmd_item *ti;
3547     char name[80];
3548     afs_int32 rank;
3549     struct setspref *ssp;
3550     int error = 0;              /* -1 means error message printed,
3551                                  * >0 means errno value for unprinted message */
3552
3553     ssp = (struct setspref *)space;
3554     ssp->flags = 0;
3555     ssp->num_servers = 0;
3556     gblob.in_size = ((char *)&(ssp->servers[0])) - (char *)ssp;
3557     gblob.in = space;
3558     gblob.out = space;
3559     gblob.out_size = AFS_PIOCTL_MAXSIZE;
3560
3561
3562     if (geteuid()) {
3563         fprintf(stderr, "Permission denied: requires root access.\n");
3564         return 1;
3565     }
3566
3567     ti = as->parms[2].items;    /* -file */
3568     if (ti) {
3569         if (debug)
3570             fprintf(stderr, "opening file %s\n", ti->data);
3571         if (!(infd = fopen(ti->data, "r"))) {
3572             perror(ti->data);
3573             error = -1;
3574         } else {
3575             while (fscanf(infd, "%79s%ld", name, &rank) != EOF) {
3576                 code = addServer(name, (unsigned short)rank);
3577                 if (code)
3578                     error = code;
3579             }
3580         }
3581     }
3582
3583     ti = as->parms[3].items;    /* -stdin */
3584     if (ti) {
3585         while (scanf("%79s%ld", name, &rank) != EOF) {
3586             code = addServer(name, (unsigned short)rank);
3587             if (code)
3588                 error = code;
3589         }
3590     }
3591
3592     for (ti = as->parms[0].items; ti; ti = ti->next) {  /* list of servers, ranks */
3593         if (ti) {
3594             if (!ti->next) {
3595                 break;
3596             }
3597             code = addServer(ti->data, (unsigned short)atol(ti->next->data));
3598             if (code)
3599                 error = code;
3600             if (debug)
3601                 printf("set fs prefs %s %s\n", ti->data, ti->next->data);
3602             ti = ti->next;
3603         }
3604     }
3605     code = pokeServers();
3606     if (code)
3607         error = code;
3608     if (debug)
3609         printf("now working on vlservers, code=%d\n", code);
3610
3611     ssp = (struct setspref *)space;
3612     ssp->flags = DBservers;
3613     ssp->num_servers = 0;
3614     gblob.in_size = ((char *)&(ssp->servers[0])) - (char *)ssp;
3615     gblob.in = space;
3616
3617     for (ti = as->parms[1].items; ti; ti = ti->next) {  /* list of dbservers, ranks */
3618         if (ti) {
3619             if (!ti->next) {
3620                 break;
3621             }
3622             code = addServer(ti->data, (unsigned short)atol(ti->next->data));
3623             if (code)
3624                 error = code;
3625             if (debug)
3626                 printf("set vl prefs %s %s\n", ti->data, ti->next->data);
3627             ti = ti->next;
3628         }
3629     }
3630
3631     if (as->parms[1].items) {
3632         if (debug)
3633             printf("now poking vlservers\n");
3634         code = pokeServers();
3635         if (code)
3636             error = code;
3637     }
3638
3639     if (error > 0)
3640         Die(error, 0);
3641
3642     return error ? 1 : 0;
3643 }
3644 #endif /* WIN32 */
3645
3646 #ifdef WIN32
3647 static int 
3648 GetPrefCmd(struct cmd_syndesc *as, void *arock)
3649 {
3650     afs_int32 code;
3651     struct cmd_item *ti;
3652     char *name, tbuffer[20];
3653     afs_int32 addr;
3654     FILE * outfd;
3655     int resolve;
3656     int vlservers;
3657     struct ViceIoctl blob;
3658     struct cm_SPrefRequest *in;
3659     struct cm_SPrefInfo *out;
3660     int i;
3661     
3662     code = 0;
3663     ti = as->parms[0].items;  /* -file */
3664     if (ti) {
3665         if (debug) fprintf(stderr,"opening file %s\n",ti->data);
3666         if (!(outfd = freopen(ti->data,"w",stdout))) {
3667             Die(errno,ti->data);
3668             return errno;
3669         }
3670     }
3671
3672     ti = as->parms[1].items;  /* -numeric */
3673     resolve = !(ti);
3674     ti = as->parms[2].items;  /* -vlservers */
3675     vlservers = (ti ? CM_SPREF_VLONLY : 0);
3676     /*  ti = as->parms[3].items;   -cell */
3677
3678     in = (struct cm_SPrefRequest *)space;
3679     in->offset = 0;
3680
3681     do {
3682         blob.in_size=sizeof(struct cm_SPrefRequest);
3683         blob.in = (char *)in;
3684         blob.out = space;
3685         blob.out_size = AFS_PIOCTL_MAXSIZE;
3686
3687         in->num_servers = (AFS_PIOCTL_MAXSIZE - 2*sizeof(short))/sizeof(struct cm_SPref);
3688         in->flags = vlservers; 
3689
3690         code = pioctl_utf8(0, VIOC_GETSPREFS, &blob, 1);
3691         if (code){
3692             perror("getserverprefs pioctl");
3693             Die (errno,0);
3694         }
3695         else {
3696             out = (struct cm_SPrefInfo *) blob.out;
3697
3698             for (i=0;i<out->num_servers;i++) {
3699                 if (resolve) {
3700                     name = hostutil_GetNameByINet(out->servers[i].host.s_addr);
3701                 }
3702                 else {
3703                     addr = ntohl(out->servers[i].host.s_addr);
3704                     sprintf(tbuffer, "%d.%d.%d.%d", (addr>>24) & 0xff, (addr>>16) & 0xff,
3705                              (addr>>8) & 0xff, addr & 0xff);
3706                     name=tbuffer;
3707                 }
3708                 printf ("%-50s %5u\n",name,out->servers[i].rank);      
3709             }
3710
3711             in->offset = out->next_offset;
3712         }
3713     } while (!code && out->next_offset > 0);
3714
3715     return code;
3716 }
3717 #else
3718 static int
3719 GetPrefCmd(struct cmd_syndesc *as, void *arock)
3720 {
3721     afs_int32 code;
3722     struct cmd_item *ti;
3723     char *name, tbuffer[20];
3724     afs_int32 rank, addr;
3725     FILE *outfd;
3726     int resolve;
3727     int vlservers = 0;
3728     struct ViceIoctl blob;
3729     struct sprefrequest *in;
3730     struct sprefinfo *out;
3731     int i;
3732
3733     ti = as->parms[0].items;    /* -file */
3734     if (ti) {
3735         if (debug)
3736             fprintf(stderr, "opening file %s\n", ti->data);
3737         if (!(outfd = freopen(ti->data, "w", stdout))) {
3738             perror(ti->data);
3739             return 1;
3740         }
3741     }
3742
3743     ti = as->parms[1].items;    /* -numeric */
3744     resolve = !(ti);
3745     ti = as->parms[2].items;    /* -vlservers */
3746     vlservers |= (ti ? DBservers : 0);
3747     /*  ti = as->parms[3].items;   -cell */
3748
3749     in = (struct sprefrequest *)space;
3750     in->offset = 0;
3751
3752     do {
3753         blob.in_size = sizeof(struct sprefrequest);
3754         blob.in = (char *)in;
3755         blob.out = space;
3756         blob.out_size = AFS_PIOCTL_MAXSIZE;
3757
3758         in->num_servers =
3759             (AFS_PIOCTL_MAXSIZE - 2 * sizeof(short)) / sizeof(struct spref);
3760         in->flags = vlservers;
3761
3762         code = pioctl_utf8(0, VIOC_GETSPREFS, &blob, 1);
3763         if (code) {
3764             perror("getserverprefs pioctl");
3765             return 1;
3766         }
3767
3768         out = (struct sprefinfo *)blob.out;
3769
3770         for (i = 0; i < out->num_servers; i++) {
3771             if (resolve) {
3772                 name = hostutil_GetNameByINet(out->servers[i].server.s_addr);
3773             } else {
3774                 addr = ntohl(out->servers[i].server.s_addr);
3775                 sprintf(tbuffer, "%d.%d.%d.%d", (addr >> 24) & 0xff,
3776                         (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff);
3777                 name = tbuffer;
3778             }
3779             printf("%-50s %5u\n", name, out->servers[i].rank);
3780         }
3781
3782         in->offset = out->next_offset;
3783     } while (out->next_offset > 0);
3784
3785     return 0;
3786 }
3787 #endif /* WIN32 */
3788
3789 static afs_int32
3790 SmbUnicodeCmd(struct cmd_syndesc * asp, void * arock)
3791 {
3792     long inValue = 0;
3793     long outValue = 0;
3794     long code;
3795
3796     struct ViceIoctl blob;
3797
3798     if (asp->parms[0].items) {
3799         /* On */
3800
3801         inValue = 3;
3802     } else if (asp->parms[1].items) {
3803         /* Off */
3804
3805         inValue = 2;
3806     }
3807
3808     if (inValue != 0 && !IsAdmin()) {
3809         fprintf (stderr, "Permission denied: Requires AFS Client Administrator access.\n");
3810         return EACCES;
3811     }
3812
3813     blob.in_size = sizeof(inValue);
3814     blob.in = (char *) &inValue;
3815     blob.out_size = sizeof(outValue);
3816     blob.out = (char *) &outValue;
3817
3818     code = pioctl_utf8(NULL, VIOC_UNICODECTL, &blob, 1);
3819     if (code) {
3820         Die(errno, NULL);
3821         return code;
3822     }
3823
3824     if (outValue != 2) {
3825         printf("Unicode support is %s%s.\n",
3826                ((outValue != 0)? "enabled":"disabled"),
3827                ((inValue != 0)? " for new SMB connections":""));
3828     } else {
3829         printf("Unicode support is absent in this installation of OpenAFS.\n");
3830     }
3831
3832     return 0;
3833 }
3834
3835 static int
3836 GetFidCmd(struct cmd_syndesc *as, void *arock)
3837 {
3838     afs_int32 code;
3839     struct ViceIoctl blob;
3840     struct cmd_item *ti;
3841     int error = 0;
3842     int literal = 0;
3843     cm_ioctlQueryOptions_t options;
3844
3845     if (as->parms[1].items)
3846         literal = 1;
3847
3848     SetDotDefault(&as->parms[0].items);
3849     for(ti=as->parms[0].items; ti; ti=ti->next) {
3850         cm_fid_t fid;
3851         afs_uint32 filetype;
3852         char cell[CELL_MAXNAMELEN];
3853
3854         /* once per file */
3855         memset(&fid, 0, sizeof(fid));
3856         memset(&options, 0, sizeof(options));
3857         filetype = 0;
3858         options.size = sizeof(options);
3859         options.field_flags |= CM_IOCTL_QOPTS_FIELD_LITERAL;
3860         options.literal = literal;
3861         blob.in_size = options.size;    /* no variable length data */
3862         blob.in = &options;
3863
3864         blob.out_size = sizeof(cm_fid_t);
3865         blob.out = (char *) &fid;
3866         if (0 == pioctl_utf8(ti->data, VIOCGETFID, &blob, 1) &&
3867             blob.out_size == sizeof(cm_fid_t)) {
3868             options.field_flags |= CM_IOCTL_QOPTS_FIELD_FID;
3869             options.fid = fid;
3870         } else {
3871             Die(errno, ti->data);
3872             error = 1;
3873             continue;
3874         }
3875
3876         blob.out_size = sizeof(filetype);
3877         blob.out = &filetype;
3878
3879         code = pioctl_utf8(ti->data, VIOC_GETFILETYPE, &blob, 1);
3880         if (code || blob.out_size != sizeof(filetype)) {
3881             Die(errno, ti->data);
3882             error = 1;
3883             continue;
3884         }
3885         blob.out_size = CELL_MAXNAMELEN;
3886         blob.out = cell;
3887
3888         code = pioctl_utf8(ti->data, VIOC_FILE_CELL_NAME, &blob, 1);
3889         if (code == 0)
3890             cell[CELL_MAXNAMELEN - 1] = '\0';
3891         printf("%s %s (%u.%u.%u) contained in cell %s\n",
3892                 filetypestr(filetype),
3893                 ti->data, fid.volume, fid.vnode, fid.unique,
3894                 code ? "unknown-cell" : cell);
3895     }
3896     return error;
3897 }
3898
3899 static int
3900 UuidCmd(struct cmd_syndesc *asp, void *arock)
3901 {
3902     long code;
3903     long inValue;
3904     afsUUID outValue;
3905     struct ViceIoctl blob;
3906     char * uuidstring = NULL;
3907
3908     if (asp->parms[0].items) {
3909 #ifdef WIN32
3910         if ( !IsAdmin() ) {
3911             fprintf (stderr,"Permission denied: requires AFS Client Administrator access.\n");
3912             return EACCES;
3913         }
3914 #else
3915         if (geteuid()) {
3916             fprintf (stderr, "Permission denied: requires root access.\n");
3917             return EACCES;
3918         }
3919 #endif
3920         inValue = 1;            /* generate new UUID */
3921     } else {
3922         inValue = 0;            /* just show the current UUID */
3923     }
3924
3925     blob.in_size = sizeof(inValue);
3926     blob.in = (char *) &inValue;
3927     blob.out_size = sizeof(outValue);
3928     blob.out = (char *) &outValue;
3929
3930     code = pioctl_utf8(NULL, VIOC_UUIDCTL, &blob, 1);
3931     if (code || blob.out_size != sizeof(outValue)) {
3932         Die(errno, NULL);
3933         return code;
3934     }
3935
3936     UuidToString((UUID *) &outValue, &uuidstring);
3937
3938     printf("%sUUID: %s",
3939            ((inValue == 1)?"New ":""),
3940            uuidstring);
3941
3942     if (uuidstring)
3943         RpcStringFree(&uuidstring);
3944
3945     return 0;
3946 }
3947
3948 static int
3949 TraceCmd(struct cmd_syndesc *asp, void *arock)
3950 {
3951     long code;
3952     struct ViceIoctl blob;
3953     long inValue;
3954     long outValue;
3955     
3956 #ifdef WIN32
3957     if ( !IsAdmin() ) {
3958         fprintf (stderr,"Permission denied: requires AFS Client Administrator access.\n");
3959         return EACCES;
3960     }
3961 #else /* WIN32 */
3962         if (geteuid()) {
3963             fprintf (stderr,"Permission denied: requires root access.\n");
3964             return EACCES;
3965         }
3966 #endif /* WIN32 */
3967
3968     if ((asp->parms[0].items && asp->parms[1].items)) {
3969         fprintf(stderr, "fs trace: must use at most one of '-off' or '-on'\n");
3970         return EINVAL;
3971     }
3972         
3973     /* determine if we're turning this tracing on or off */
3974     inValue = 0;
3975     if (asp->parms[0].items)
3976         inValue = 3;            /* enable */
3977     else if (asp->parms[1].items) 
3978         inValue = 2;    /* disable */
3979     if (asp->parms[2].items) 
3980         inValue |= 4;           /* do reset */
3981     if (asp->parms[3].items) 
3982         inValue |= 8;           /* dump */
3983         
3984     blob.in_size = sizeof(long);
3985     blob.in = (char *) &inValue;
3986     blob.out_size = sizeof(long);
3987     blob.out = (char *) &outValue;
3988         
3989     code = pioctl_utf8(NULL, VIOC_TRACECTL, &blob, 1);
3990     if (code || blob.out_size != sizeof(long)) {
3991         Die(errno, NULL);
3992         return code;
3993     }
3994
3995     if (outValue) 
3996         printf("AFS tracing enabled.\n");
3997     else 
3998         printf("AFS tracing disabled.\n");
3999
4000     return 0;
4001 }
4002
4003 static void sbusage(void)
4004 {
4005     fprintf(stderr, "example usage: %s storebehind -files *.o -kb 99999 -default 0\n", pn);
4006     fprintf(stderr, "               %s sb 50000 *.[ao] -default 10\n", pn);
4007 }       
4008
4009 /* fs sb -kbytes 9999 -files *.o -default 64 */
4010 static int
4011 StoreBehindCmd(struct cmd_syndesc *as, void *arock)
4012 {
4013     afs_int32 code = 0;
4014     struct ViceIoctl blob;
4015     struct cmd_item *ti;
4016     struct sbstruct tsb, tsb2;
4017     int verbose = 0;
4018     afs_int32 allfiles;
4019     char *t;
4020     int error = 0;
4021
4022 #ifdef WIN32
4023     if ( !IsAdmin() ) {
4024         fprintf (stderr,"Permission denied: requires AFS Client Administrator access.\n");  
4025         return EACCES;
4026     }
4027 #endif /* WIN32 */
4028
4029     tsb.sb_thisfile = -1;
4030     ti = as->parms[0].items;    /* -kbytes */
4031     if (ti) {
4032         if (!as->parms[1].items) {
4033             fprintf(stderr, "%s: you must specify -files with -kbytes.\n",
4034                     pn);
4035             return 1;
4036         }
4037         tsb.sb_thisfile = strtol(ti->data, &t, 10) * 1024;
4038         if ((tsb.sb_thisfile < 0) || (t != ti->data + strlen(ti->data))) {
4039             fprintf(stderr, "%s: %s must be 0 or a positive number.\n", pn,
4040                     ti->data);
4041             return 1;
4042         }
4043     }
4044
4045     allfiles = tsb.sb_default = -1;     /* Don't set allfiles yet */
4046     ti = as->parms[2].items;    /* -allfiles */
4047     if (ti) {
4048         allfiles = strtol(ti->data, &t, 10) * 1024;
4049         if ((allfiles < 0) || (t != ti->data + strlen(ti->data))) {
4050             fprintf(stderr, "%s: %s must be 0 or a positive number.\n", pn,
4051                     ti->data);
4052             return 1;
4053         }
4054     }
4055
4056     /* -verbose or -file only or no options */
4057     if (as->parms[3].items || (as->parms[1].items && !as->parms[0].items)
4058         || (!as->parms[0].items && !as->parms[1].items
4059             && !as->parms[2].items))
4060         verbose = 1;
4061
4062     blob.in = (char *)&tsb;
4063     blob.out = (char *)&tsb2;
4064     blob.in_size = blob.out_size = sizeof(struct sbstruct);
4065     memset(&tsb2, 0, sizeof(tsb2));
4066
4067     /* once per -file */
4068     for (ti = as->parms[1].items; ti; ti = ti->next) {
4069         /* Do this solely to see if the file is there */
4070         code = pioctl_utf8(ti->data, VIOCWHEREIS, &blob, 1);
4071         if (code) {
4072             Die(errno, ti->data);
4073             error = 1;
4074             continue;
4075         }
4076
4077         code = pioctl_utf8(ti->data, VIOC_STOREBEHIND, &blob, 1);
4078         if (code) {
4079             Die(errno, ti->data);
4080             error = 1;
4081             continue;
4082         }
4083
4084         if (verbose && (blob.out_size == sizeof(tsb2))) {
4085             if (tsb2.sb_thisfile == -1) {
4086                 fprintf(stdout, "Will store %s according to default.\n",
4087                         ti->data);
4088             } else {
4089                 fprintf(stdout,
4090                         "Will store up to %d kbytes of %s asynchronously.\n",
4091                         (tsb2.sb_thisfile / 1024), ti->data);
4092             }
4093         }
4094     }
4095
4096     /* If no files - make at least one pioctl call, or
4097      * set the allfiles default if we need to.
4098      */
4099     if (!as->parms[1].items || (allfiles != -1)) {
4100         tsb.sb_default = allfiles;
4101         code = pioctl_utf8(0, VIOC_STOREBEHIND, &blob, 1);
4102         if (code) {
4103             Die(errno, ((allfiles == -1) ? 0 : "-allfiles"));
4104             error = 1;
4105         }
4106     }
4107
4108     /* Having no arguments also reports the default store asynchrony */
4109     if (verbose && (blob.out_size == sizeof(tsb2))) {
4110         fprintf(stdout, "Default store asynchrony is %d kbytes.\n",
4111                 (tsb2.sb_default / 1024));
4112     }
4113
4114     return error;
4115 }
4116
4117 static afs_int32 
4118 SetCryptCmd(struct cmd_syndesc *as, void *arock)
4119 {
4120     afs_int32 code = 0, flag;
4121     struct ViceIoctl blob;
4122     char *tp;
4123  
4124 #ifdef WIN32
4125     if ( !IsAdmin() ) {
4126         fprintf (stderr,"Permission denied: requires AFS Client Administrator access.\n");
4127         return EACCES;
4128     }
4129 #endif /* WIN32 */
4130
4131     tp = as->parms[0].items->data;
4132     if (strcmp(tp, "on") == 0)
4133       flag = 1;
4134     else if (strcmp(tp, "off") == 0)
4135       flag = 0;
4136     else if (strcmp(tp, "auth") == 0)
4137       flag = 2;
4138     else {
4139       fprintf (stderr, "%s: %s must be \"on\", \"auth\", or \"off\".\n", pn, tp);
4140       return EINVAL;
4141     }
4142
4143     blob.in = (char *) &flag;
4144     blob.in_size = sizeof(flag);
4145     blob.out_size = 0;
4146     code = pioctl_utf8(0, VIOC_SETRXKCRYPT, &blob, 1);
4147     if (code)
4148         Die(code, NULL);
4149     return 0;
4150 }
4151
4152 static afs_int32 
4153 GetCryptCmd(struct cmd_syndesc *as, void *arock)
4154 {
4155     afs_int32 code = 0, flag;
4156     struct ViceIoctl blob;
4157     char *tp;
4158  
4159     blob.in = NULL;
4160     blob.in_size = 0;
4161     blob.out_size = sizeof(flag);
4162     blob.out = space;
4163
4164     code = pioctl_utf8(0, VIOC_GETRXKCRYPT, &blob, 1);
4165
4166     if (code || blob.out_size != sizeof(flag))
4167         Die(code, NULL);
4168     else {
4169       tp = space;
4170       memcpy(&flag, tp, sizeof(afs_int32));
4171       printf("Security level is currently ");
4172       if (flag == 2)
4173           printf("auth (data integrity).\n");
4174       else if (flag == 1)
4175         printf("crypt (data security).\n");
4176       else
4177         printf("clear.\n");
4178     }
4179     return 0;
4180 }
4181
4182 static int
4183 MemDumpCmd(struct cmd_syndesc *asp, void *arock)
4184 {
4185     long code;
4186     struct ViceIoctl blob;
4187     long inValue = 0;
4188     long outValue;
4189
4190     if ( !IsAdmin() ) {
4191         fprintf (stderr,"Permission denied: requires AFS Client Administrator access.\n");
4192         return EACCES;
4193     }
4194
4195     if ((asp->parms[0].items && asp->parms[1].items)) {
4196         fprintf(stderr, "%s trace: must use at most one of '-begin' or '-end'\n", pn);
4197         return EINVAL;
4198     }
4199
4200     /* determine if we're turning this tracing on or off */
4201     if (asp->parms[0].items)
4202         inValue = 1;            /* begin */
4203     else if (asp->parms[1].items)
4204         inValue = 0;            /* end */
4205
4206
4207     blob.in_size = sizeof(long);
4208     blob.in = (char *) &inValue;
4209     blob.out_size = sizeof(long);
4210     blob.out = (char *) &outValue;
4211
4212     code = pioctl_utf8(NULL, VIOC_TRACEMEMDUMP, &blob, 1);
4213     if (code || blob.out_size != sizeof(long)) {
4214         Die(errno, NULL);
4215         return code;
4216     }
4217
4218     if (!outValue) { 
4219         printf("AFS memdump created.\n");
4220         return 0;
4221     } else {
4222         printf("AFS memdump failed.\n");
4223         return -1;
4224     }
4225 }
4226
4227 static int
4228 MiniDumpCmd(struct cmd_syndesc *asp, void *arock)
4229 {
4230     BOOL success = 0;
4231     SERVICE_STATUS status;
4232     SC_HANDLE hManager = NULL;
4233     SC_HANDLE hService = NULL;
4234
4235     if ( !IsAdmin() ) {
4236         fprintf (stderr,"Permission denied: requires AFS Client Administrator access.\n");
4237         return EACCES;
4238     }
4239
4240     hManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
4241     if (!hManager)
4242         goto failure;
4243
4244     hService = OpenService(hManager, "TransarcAFSDaemon", SERVICE_USER_DEFINED_CONTROL);
4245     if (!hService)
4246         goto failure;
4247
4248     success = ControlService(hService, SERVICE_CONTROL_CUSTOM_DUMP, &status);
4249
4250     if (success) {
4251         CloseServiceHandle(hService);
4252         CloseServiceHandle(hManager);
4253
4254         printf("AFS minidump generated.\n");
4255         return 0;
4256     }
4257
4258   failure: 
4259     if (hService)
4260         CloseServiceHandle(hService);
4261     if (hManager)
4262         CloseServiceHandle(hManager);
4263
4264     printf("AFS minidump failed.\n");
4265     return -1;
4266 }
4267
4268 static int
4269 CSCPolicyCmd(struct cmd_syndesc *asp, void *arock)
4270 {
4271     struct cmd_item *ti;
4272     char *share = NULL;
4273     HKEY hkCSCPolicy;
4274
4275     if ( !IsAdmin() ) {
4276         fprintf (stderr,"Permission denied: requires AFS Client Administrator access.\n");
4277         return EACCES;
4278     }
4279
4280     for(ti=asp->parms[0].items; ti;ti=ti->next) {
4281         share = ti->data;
4282         if (share)
4283         {
4284             break;
4285         }
4286     }
4287
4288     if (share)
4289     {
4290         char *policy;
4291
4292         RegCreateKeyEx( HKEY_LOCAL_MACHINE, 
4293                          AFSREG_CLT_OPENAFS_SUBKEY "\\CSCPolicy",
4294                         0, 
4295                         "AFS", 
4296                         REG_OPTION_NON_VOLATILE,
4297                         (IsWow64()?KEY_WOW64_64KEY:0)|KEY_WRITE,
4298                         NULL, 
4299                         &hkCSCPolicy,
4300                         NULL );
4301
4302         if ( hkCSCPolicy == NULL ) {
4303             fprintf (stderr,"Permission denied: requires Administrator access.\n");
4304             return EACCES;
4305         }
4306
4307         if ( !IsAdmin() ) {
4308             fprintf (stderr,"Permission denied: requires AFS Client Administrator access.\n");
4309             RegCloseKey(hkCSCPolicy);
4310             return EACCES;
4311         }
4312
4313         policy = "manual";
4314                 
4315         if (asp->parms[1].items)
4316             policy = "manual";
4317         if (asp->parms[2].items)
4318             policy = "programs";
4319         if (asp->parms[3].items)
4320             policy = "documents";
4321         if (asp->parms[4].items)
4322             policy = "disable";
4323                 
4324         RegSetValueEx( hkCSCPolicy, share, 0, REG_SZ, policy, (DWORD)strlen(policy)+1);
4325                 
4326         printf("CSC policy on share \"%s\" changed to \"%s\".\n\n", share, policy);
4327         printf("Close all applications that accessed files on this share or restart AFS Client for the change to take effect.\n"); 
4328     }
4329     else
4330     {
4331         DWORD dwIndex, dwPolicies;
4332         char policyName[256];
4333         DWORD policyNameLen;
4334         char policy[256];
4335         DWORD policyLen;
4336         DWORD dwType;
4337
4338         /* list current csc policies */
4339
4340         RegCreateKeyEx( HKEY_LOCAL_MACHINE, 
4341                         AFSREG_CLT_OPENAFS_SUBKEY "\\CSCPolicy",
4342                         0, 
4343                         "AFS", 
4344                         REG_OPTION_NON_VOLATILE,
4345                         (IsWow64()?KEY_WOW64_64KEY:0)|KEY_READ|KEY_QUERY_VALUE,
4346                         NULL, 
4347                         &hkCSCPolicy,
4348                         NULL );
4349
4350         RegQueryInfoKey( hkCSCPolicy,
4351                          NULL,  /* lpClass */
4352                          NULL,  /* lpcClass */
4353                          NULL,  /* lpReserved */
4354                          NULL,  /* lpcSubKeys */
4355                          NULL,  /* lpcMaxSubKeyLen */
4356                          NULL,  /* lpcMaxClassLen */
4357                          &dwPolicies, /* lpcValues */
4358                          NULL,  /* lpcMaxValueNameLen */
4359                          NULL,  /* lpcMaxValueLen */
4360                          NULL,  /* lpcbSecurityDescriptor */
4361                          NULL   /* lpftLastWriteTime */
4362                          );
4363                 
4364         printf("Current CSC policies:\n");
4365         for ( dwIndex = 0; dwIndex < dwPolicies; dwIndex ++ ) {
4366
4367             policyNameLen = sizeof(policyName);
4368             policyLen = sizeof(policy);
4369             RegEnumValue( hkCSCPolicy, dwIndex, policyName, &policyNameLen, NULL,
4370                           &dwType, policy, &policyLen);
4371
4372             printf("  %s = %s\n", policyName, policy);
4373         }
4374     }
4375
4376     RegCloseKey(hkCSCPolicy);
4377     return (0);
4378 }
4379
4380 #ifndef WIN32
4381 /* get clients interface addresses */
4382 static int
4383 GetClientAddrsCmd(struct cmd_syndesc *as, void *arock)
4384 {
4385     afs_int32 code;
4386     struct cmd_item *ti;
4387     char *name;
4388     struct ViceIoctl blob;
4389     struct sprefrequest *in;
4390     struct sprefinfo *out;
4391
4392     in = (struct sprefrequest *)space;
4393     in->offset = 0;
4394
4395     do {
4396         blob.in_size = sizeof(struct sprefrequest);
4397         blob.in = (char *)in;
4398         blob.out = space;
4399         blob.out_size = AFS_PIOCTL_MAXSIZE;
4400
4401         in->num_servers =
4402             (AFS_PIOCTL_MAXSIZE - 2 * sizeof(short)) / sizeof(struct spref);
4403         /* returns addr in network byte order */
4404         code = pioctl_utf8(0, VIOC_GETCPREFS, &blob, 1);
4405         if (code) {
4406             perror("getClientInterfaceAddr pioctl");
4407             return 1;
4408         }
4409
4410         {
4411             int i;
4412             out = (struct sprefinfo *)blob.out;
4413             for (i = 0; i < out->num_servers; i++) {
4414                 afs_int32 addr;
4415                 char tbuffer[32];
4416                 addr = ntohl(out->servers[i].server.s_addr);
4417                 sprintf(tbuffer, "%d.%d.%d.%d", (addr >> 24) & 0xff,
4418                         (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff);
4419                 printf("%-50s\n", tbuffer);
4420             }
4421             in->offset = out->next_offset;
4422         }
4423     } while (out->next_offset > 0);
4424
4425     return 0;
4426 }
4427
4428 static int
4429 SetClientAddrsCmd(struct cmd_syndesc *as, void *arock)
4430 {
4431     afs_int32 code, addr;
4432     struct cmd_item *ti;
4433     char name[80];
4434     struct ViceIoctl blob;
4435     struct setspref *ssp;
4436     int sizeUsed = 0, i, flag;
4437     afs_int32 existingAddr[1024];       /* existing addresses on this host */
4438     int existNu;
4439     int error = 0;
4440
4441     ssp = (struct setspref *)space;
4442     ssp->num_servers = 0;
4443     blob.in = space;
4444     blob.out = space;
4445     blob.out_size = AFS_PIOCTL_MAXSIZE;
4446
4447     if (geteuid()) {
4448         fprintf(stderr, "Permission denied: requires root access.\n");
4449         return 1;
4450     }
4451
4452     /* extract all existing interface addresses */
4453     existNu = rx_getAllAddr(existingAddr, 1024);
4454     if (existNu < 0)
4455         return 1;
4456
4457     sizeUsed = sizeof(struct setspref); /* space used in ioctl buffer */
4458     for (ti = as->parms[0].items; ti; ti = ti->next) {
4459         if (sizeUsed >= sizeof(space)) {
4460             fprintf(stderr, "No more space\n");
4461             return 1;
4462         }
4463         addr = extractAddr(ti->data, 20);       /* network order */
4464         if ((addr == AFS_IPINVALID) || (addr == AFS_IPINVALIDIGNORE)) {
4465             fprintf(stderr, "Error in specifying address: %s..ignoring\n",
4466                     ti->data);
4467             error = 1;
4468             continue;
4469         }
4470         /* see if it is an address that really exists */
4471         for (flag = 0, i = 0; i < existNu; i++)
4472             if (existingAddr[i] == addr) {
4473                 flag = 1;
4474                 break;
4475             }
4476         if (!flag) {            /* this is an nonexistent address */
4477             fprintf(stderr, "Nonexistent address: 0x%08x..ignoring\n", addr);
4478             error = 1;
4479             continue;
4480         }
4481         /* copy all specified addr into ioctl buffer */
4482         (ssp->servers[ssp->num_servers]).server.s_addr = addr;
4483         printf("Adding 0x%08x\n", addr);
4484         ssp->num_servers++;
4485         sizeUsed += sizeof(struct spref);
4486     }
4487     if (ssp->num_servers < 1) {
4488         fprintf(stderr, "No addresses specified\n");
4489         return 1;
4490     }
4491     blob.in_size = sizeUsed - sizeof(struct spref);
4492
4493     code = pioctl_utf8(0, VIOC_SETCPREFS, &blob, 1);    /* network order */
4494     if (code) {
4495         Die(errno, 0);
4496         error = 1;
4497     }
4498
4499     return error;
4500 }
4501
4502 static int
4503 FlushMountCmd(struct cmd_syndesc *as, void *arock)
4504 {
4505     afs_int32 code;
4506     struct ViceIoctl blob;
4507     struct cmd_item *ti;
4508     char orig_name[1024];       /*Original name, may be modified */
4509     char true_name[1024];       /*``True'' dirname (e.g., symlink target) */
4510     char parent_dir[1024];      /*Parent directory of true name */
4511     char *last_component;       /*Last component of true name */
4512     struct stat statbuff;       /*Buffer for status info */
4513     int link_chars_read;        /*Num chars read in readlink() */
4514     int thru_symlink;           /*Did we get to a mount point via a symlink? */
4515     int error = 0;
4516
4517     for (ti = as->parms[0].items; ti; ti = ti->next) {
4518         /* once per file */
4519         thru_symlink = 0;
4520         sprintf(orig_name, "%s%s", (ti->data[0] == '/') ? "" : "./",
4521                 ti->data);
4522
4523         if (lstat(orig_name, &statbuff) < 0) {
4524             /* if lstat fails, we should still try the pioctl, since it
4525              * may work (for example, lstat will fail, but pioctl will
4526              * work if the volume of offline (returning ENODEV). */
4527             statbuff.st_mode = S_IFDIR; /* lie like pros */
4528         }
4529
4530         /*
4531          * The lstat succeeded.  If the given file is a symlink, substitute
4532          * the file name with the link name.
4533          */
4534         if ((statbuff.st_mode & S_IFMT) == S_IFLNK) {
4535             thru_symlink = 1;
4536             /*
4537              * Read name of resolved file.
4538              */
4539             link_chars_read = readlink(orig_name, true_name, 1024);
4540             if (link_chars_read <= 0) {
4541                 fprintf(stderr,
4542                         "%s: Can't read target name for '%s' symbolic link!\n",
4543                         pn, orig_name);
4544                 error = 1;
4545                 continue;
4546             }
4547
4548             /*
4549              * Add a trailing null to what was read, bump the length.
4550              */
4551             true_name[link_chars_read++] = 0;
4552
4553             /*
4554              * If the symlink is an absolute pathname, we're fine.  Otherwise, we
4555              * have to create a full pathname using the original name and the
4556              * relative symlink name.  Find the rightmost slash in the original
4557              * name (we know there is one) and splice in the symlink value.
4558              */
4559             if (true_name[0] != '/') {
4560                 last_component = (char *)strrchr(orig_name, '/');
4561                 strcpy(++last_component, true_name);
4562                 strcpy(true_name, orig_name);
4563             }
4564         } else
4565             strcpy(true_name, orig_name);
4566
4567         /*
4568          * Find rightmost slash, if any.
4569          */
4570         last_component = (char *)strrchr(true_name, '/');
4571         if (last_component) {
4572             /*
4573              * Found it.  Designate everything before it as the parent directory,
4574              * everything after it as the final component.
4575              */
4576             strncpy(parent_dir, true_name, last_component - true_name);
4577             parent_dir[last_component - true_name] = 0;
4578             last_component++;   /*Skip the slash */
4579         } else {
4580             /*
4581              * No slash appears in the given file name.  Set parent_dir to the current
4582              * directory, and the last component as the given name.
4583              */
4584             strcpy(parent_dir, ".");
4585             last_component = true_name;
4586         }
4587
4588         if (strcmp(last_component, ".") == 0
4589             || strcmp(last_component, "..") == 0) {
4590             fprintf(stderr,
4591                     "%s: you may not use '.' or '..' as the last component\n",
4592                     pn);
4593             fprintf(stderr, "%s: of a name in the 'fs flushmount' command.\n",
4594                     pn);
4595             error = 1;
4596             continue;
4597         }
4598
4599         blob.in = last_component;
4600         blob.in_size = strlen(last_component) + 1;
4601         blob.out_size = 0;
4602         memset(space, 0, AFS_PIOCTL_MAXSIZE);
4603
4604         code = pioctl_utf8(parent_dir, VIOC_AFS_FLUSHMOUNT, &blob, 1);
4605
4606         if (code != 0) {
4607             if (errno == EINVAL) {
4608                 fprintf(stderr, "'%s' is not a mount point.\n", ti->data);
4609             } else {
4610                 Die(errno, (ti->data ? ti->data : parent_dir));
4611             }
4612             error = 1;
4613         }
4614     }
4615     return error;
4616 }
4617 #endif /* WIN32 */
4618
4619 static int
4620 RxStatProcCmd(struct cmd_syndesc *as, void *arock)
4621 {
4622     afs_int32 code;
4623     afs_int32 flags = 0;
4624     struct ViceIoctl blob;
4625
4626     if (as->parms[0].items) {   /* -enable */
4627         flags |= AFSCALL_RXSTATS_ENABLE;
4628     }
4629     if (as->parms[1].items) {   /* -disable */
4630         flags |= AFSCALL_RXSTATS_DISABLE;
4631     }
4632     if (as->parms[2].items) {   /* -clear */
4633         flags |= AFSCALL_RXSTATS_CLEAR;
4634     }
4635     if (flags == 0) {
4636         fprintf(stderr, "You must specify at least one argument\n");
4637         return 1;
4638     }
4639
4640     blob.in = (char *)&flags;
4641     blob.in_size = sizeof(afs_int32);
4642     blob.out_size = 0;
4643
4644     code = pioctl_utf8(NULL, VIOC_RXSTAT_PROC, &blob, 1);
4645     if (code != 0) {
4646         Die(errno, NULL);
4647         return 1;
4648     }
4649
4650     return 0;
4651 }
4652
4653 static int
4654 RxStatPeerCmd(struct cmd_syndesc *as, void *arock)
4655 {
4656     afs_int32 code;
4657     afs_int32 flags = 0;
4658     struct ViceIoctl blob;
4659
4660     if (as->parms[0].items) {   /* -enable */
4661         flags |= AFSCALL_RXSTATS_ENABLE;
4662     }
4663     if (as->parms[1].items) {   /* -disable */
4664         flags |= AFSCALL_RXSTATS_DISABLE;
4665     }
4666     if (as->parms[2].items) {   /* -clear */
4667         flags |= AFSCALL_RXSTATS_CLEAR;
4668     }
4669     if (flags == 0) {
4670         fprintf(stderr, "You must specify at least one argument\n");
4671         return 1;
4672     }
4673
4674     blob.in = (char *)&flags;
4675     blob.in_size = sizeof(afs_int32);
4676     blob.out_size = 0;
4677
4678     code = pioctl_utf8(NULL, VIOC_RXSTAT_PEER, &blob, 1);
4679     if (code != 0) {
4680         Die(errno, NULL);
4681         return 1;
4682     }
4683
4684     return 0;
4685 }
4686
4687 static int
4688 TestVolStatCmd(struct cmd_syndesc *as, void *arock)
4689 {
4690     afs_int32 code;
4691     struct VolStatTest test;
4692     struct ViceIoctl blob;
4693     char * tp;
4694     afs_uint32 n;
4695
4696     memset(&test, 0, sizeof(test));
4697
4698     if (as->parms[0].items) {   /* -network */
4699         tp = as->parms[0].items->data;
4700         if (strcmp(tp, "up") == 0)
4701             test.flags |= VOLSTAT_TEST_NETWORK_UP;
4702         else if (strcmp(tp, "down") == 0)
4703             test.flags |= VOLSTAT_TEST_NETWORK_DOWN;
4704         else {
4705             fprintf (stderr, "%s: %s must be \"up\" or \"down\".\n", pn, tp);
4706             return EINVAL;
4707         }
4708     }
4709     if (as->parms[1].items) {   /* check */
4710         test.flags |= VOLSTAT_TEST_CHECK_VOLUME;
4711     }
4712     if (as->parms[2].items) {   /* cell */
4713         tp = as->parms[2].items->data;
4714         n = atoi(tp);
4715         if (n != 0)
4716             test.fid.cell = n;
4717         else {
4718             strncpy(test.cellname, tp, sizeof(test.cellname));
4719             test.cellname[sizeof(test.cellname)-1] = '\0';
4720         }
4721     }
4722     if (as->parms[3].items) {   /* volume */
4723         tp = as->parms[3].items->data;
4724         n = atoi(tp);
4725         if (n != 0)
4726             test.fid.volume = n;
4727         else {
4728             strncpy(test.volname, tp, sizeof(test.volname));
4729             test.volname[sizeof(test.volname)-1] = '\0';
4730         }
4731     }
4732     if (as->parms[4].items) {   /* state */
4733         tp = as->parms[4].items->data;
4734         if (strcmp(tp, "online") == 0)
4735             test.state = vl_online;
4736         else if (strcmp(tp, "busy") == 0)
4737             test.state = vl_busy;
4738         else if (strcmp(tp, "offline") == 0)
4739             test.state = vl_offline;
4740         else if (strcmp(tp, "down") == 0)
4741             test.state = vl_alldown;
4742         else {
4743             fprintf (stderr, "%s: %s must be \"online\", \"busy\", \"offline\" or \"down\".\n", pn, tp);
4744             return EINVAL;
4745         }
4746     }
4747
4748     if ((test.fid.cell || test.cellname[0]) && !(test.fid.volume || test.volname[0]) ||
4749          !(test.fid.cell || test.cellname[0]) && (test.fid.volume || test.volname[0])) {
4750         fprintf (stderr, "%s: both a cell and a volume must be specified.\n", pn, tp);
4751         return EINVAL;
4752     }
4753
4754     blob.in = (char *)&test;
4755     blob.in_size = sizeof(test);
4756     blob.out_size = 0;
4757
4758     code = pioctl_utf8(NULL, VIOC_VOLSTAT_TEST, &blob, 1);
4759     if (code != 0) {
4760         Die(errno, NULL);
4761         return 1;
4762     }
4763
4764     return 0;
4765 }
4766
4767 static int 
4768 ChOwnCmd(struct cmd_syndesc *as, void *arock)
4769 {
4770     afs_int32 code;
4771     struct ViceIoctl blob;
4772     struct cmd_item *ti;
4773     int error = 0;
4774     int literal = 0;
4775     struct { 
4776         cm_ioctlQueryOptions_t options;
4777         afs_uint32 owner;
4778     } inData;
4779     afs_uint32 ownerId;
4780     char * ownerStr;
4781     char confDir[257];
4782
4783     cm_GetConfigDir(confDir, sizeof(confDir));
4784
4785     if (as->parms[2].items)
4786         literal = 1;
4787
4788     ownerStr = as->parms[0].items->data;
4789     ownerId = atoi(ownerStr);
4790
4791     SetDotDefault(&as->parms[1].items);
4792     for(ti=as->parms[1].items; ti; ti=ti->next) {
4793         cm_fid_t fid;
4794         afs_uint32 filetype;
4795         char cell[CELL_MAXNAMELEN];
4796
4797         /* once per file */
4798         memset(&fid, 0, sizeof(fid));
4799         memset(&inData, 0, sizeof(inData));
4800         filetype = 0;
4801         inData.options.size = sizeof(inData.options);
4802         inData.options.field_flags |= CM_IOCTL_QOPTS_FIELD_LITERAL;
4803         inData.options.literal = literal;
4804         blob.in_size = inData.options.size;    /* no variable length data */
4805         blob.in = &inData;
4806
4807         blob.out_size = sizeof(cm_fid_t);
4808         blob.out = (char *) &fid;
4809         if (0 == pioctl_utf8(ti->data, VIOCGETFID, &blob, 1) &&
4810             blob.out_size == sizeof(cm_fid_t)) {
4811             inData.options.field_flags |= CM_IOCTL_QOPTS_FIELD_FID;
4812             inData.options.fid = fid;
4813         } else {
4814             Die(errno, ti->data);
4815             error = 1;
4816             continue;
4817         }
4818
4819         /* 
4820          * if the owner was specified as a numeric value,
4821          * then we can just use it.  Otherwise, we need 
4822          * to know the cell of the path to determine which
4823          * ptserver to contact in order to convert the name
4824          * to a numeric value.
4825          */
4826         if (ownerId == 0) {
4827             blob.out_size = CELL_MAXNAMELEN;
4828             blob.out = cell;
4829
4830             code = pioctl_utf8(ti->data, VIOC_FILE_CELL_NAME, &blob, 1);
4831             if (code) {
4832                 Die(errno, ti->data);
4833                 error = 1;
4834                 continue;
4835             }
4836             cell[CELL_MAXNAMELEN - 1] = '\0';
4837             /* 
4838              * We now know the cell for the target and we need to
4839              * convert the ownerStr to the Id for this user 
4840              */
4841             pr_Initialize(1, confDir, cell);
4842             code = pr_SNameToId(ownerStr, &inData.owner);
4843             pr_End();
4844
4845             if (code || inData.owner == ANONYMOUSID ) {
4846                 Die(ECHILD, ti->data);
4847                 error = 1;
4848                 continue;
4849             }
4850         } else {
4851             inData.owner = ownerId;
4852         }
4853
4854         blob.in_size = sizeof(inData);
4855         blob.out = NULL;
4856         blob.out_size = 0;
4857         code = pioctl_utf8(ti->data, VIOC_SETOWNER, &blob, 1);
4858         if (code) {
4859             Die(errno, ti->data);
4860         }
4861     }
4862     return error;
4863 }
4864
4865 static int 
4866 ChGrpCmd(struct cmd_syndesc *as, void *arock)
4867 {
4868     afs_int32 code;
4869     struct ViceIoctl blob;
4870     struct cmd_item *ti;
4871     int error = 0;
4872     int literal = 0;
4873     struct { 
4874         cm_ioctlQueryOptions_t options;
4875         afs_uint32 group;
4876     } inData;
4877     afs_uint32 groupId;
4878     char * groupStr;
4879     char confDir[257];
4880
4881     cm_GetConfigDir(confDir, sizeof(confDir));
4882
4883     if (as->parms[2].items)
4884         literal = 1;
4885
4886     groupStr = as->parms[0].items->data;
4887     groupId = atoi(groupStr);
4888
4889     SetDotDefault(&as->parms[1].items);
4890     for(ti=as->parms[1].items; ti; ti=ti->next) {
4891         cm_fid_t fid;
4892         afs_uint32 filetype;
4893         char cell[CELL_MAXNAMELEN];
4894
4895         /* once per file */
4896         memset(&fid, 0, sizeof(fid));
4897         memset(&inData, 0, sizeof(inData));
4898         filetype = 0;
4899         inData.options.size = sizeof(inData.options);
4900         inData.options.field_flags |= CM_IOCTL_QOPTS_FIELD_LITERAL;
4901         inData.options.literal = literal;
4902         blob.in_size = inData.options.size;    /* no variable length data */
4903         blob.in = &inData;
4904
4905         blob.out_size = sizeof(cm_fid_t);
4906         blob.out = (char *) &fid;
4907         if (0 == pioctl_utf8(ti->data, VIOCGETFID, &blob, 1) &&
4908             blob.out_size == sizeof(cm_fid_t)) {
4909             inData.options.field_flags |= CM_IOCTL_QOPTS_FIELD_FID;
4910             inData.options.fid = fid;
4911         } else {
4912             Die(errno, ti->data);
4913             error = 1;
4914             continue;
4915         }
4916
4917         /* 
4918          * if the group was specified as a numeric value,
4919          * then we can just use it.  Otherwise, we need 
4920          * to know the cell of the path to determine which
4921          * ptserver to contact in order to convert the name
4922          * to a numeric value.
4923          */
4924         if (groupId == 0) {
4925             blob.out_size = CELL_MAXNAMELEN;
4926             blob.out = cell;
4927
4928             code = pioctl_utf8(ti->data, VIOC_FILE_CELL_NAME, &blob, 1);
4929             if (code) {
4930                 Die(errno, ti->data);
4931                 error = 1;
4932                 continue;
4933             }
4934             cell[CELL_MAXNAMELEN - 1] = '\0';
4935             /* 
4936              * We now know the cell for the target and we need to
4937              * convert the groupStr to the Id for this user 
4938              */
4939             pr_Initialize(1, confDir, cell);
4940             code = pr_SNameToId(groupStr, &inData.group);
4941             pr_End();
4942
4943             if (code || inData.group == ANONYMOUSID ) {
4944                 Die(ECHILD, ti->data);
4945                 error = 1;
4946                 continue;
4947             }
4948         } else {
4949             inData.group = groupId;
4950         }
4951
4952         blob.in_size = sizeof(inData);
4953         blob.out = NULL;
4954         blob.out_size = 0;
4955         code = pioctl_utf8(ti->data, VIOC_SETGROUP, &blob, 1);
4956         if (code) {
4957             Die(errno, ti->data);
4958         }
4959     }
4960     return error;
4961 }
4962
4963 #ifndef WIN32
4964 #include "AFS_component_version_number.c"
4965 #endif
4966
4967 static void
4968 FreeUtf8CmdLine(int argc, char ** argv)
4969 {
4970     int i;
4971     for (i=0; i < argc; i++) {
4972         if (argv[i])
4973             free(argv[i]);
4974     }
4975     free(argv);
4976 }
4977
4978 static char **
4979 MakeUtf8Cmdline(int argc, const wchar_t **wargv)
4980 {
4981     char ** argv;
4982     int i;
4983
4984     argv = calloc(argc, sizeof(argv[0]));
4985     if (argv == NULL)
4986         return NULL;
4987
4988     for (i=0; i < argc; i++) {
4989         int s;
4990
4991         s = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, NULL, 0, NULL, FALSE);
4992         if (s == 0 ||
4993             (argv[i] = calloc(s+1, sizeof(char))) == NULL) {
4994             break;
4995         }
4996
4997         s = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, argv[i], s+1, NULL, FALSE);
4998         if (s == 0) {
4999             break;
5000         }
5001     }
5002
5003     if (i < argc) {
5004         FreeUtf8CmdLine(argc, argv);
5005         return NULL;
5006     }
5007
5008     return argv;
5009 }
5010
5011 int wmain(int argc, wchar_t **wargv)
5012 {
5013     afs_int32 code;
5014     struct cmd_syndesc *ts;
5015     char ** argv;
5016
5017 #ifdef  AFS_AIX32_ENV
5018     /*
5019      * The following signal action for AIX is necessary so that in case of a 
5020      * crash (i.e. core is generated) we can include the user's data section 
5021      * in the core dump. Unfortunately, by default, only a partial core is
5022      * generated which, in many cases, isn't too useful.
5023      */
5024     struct sigaction nsa;
5025     
5026     sigemptyset(&nsa.sa_mask);
5027     nsa.sa_handler = SIG_DFL;
5028     nsa.sa_flags = SA_FULLDUMP;
5029     sigaction(SIGSEGV, &nsa, NULL);
5030 #endif
5031
5032 #ifdef WIN32
5033     WSADATA WSAjunk;
5034     WSAStartup(0x0101, &WSAjunk);
5035 #endif /* WIN32 */
5036
5037     argv = MakeUtf8Cmdline(argc, wargv);
5038
5039     /* try to find volume location information */
5040     osi_Init();
5041
5042 #ifndef WIN32
5043     ts = cmd_CreateSyntax("getclientaddrs", GetClientAddrsCmd, NULL,
5044                           "get client network interface addresses");
5045     cmd_CreateAlias(ts, "gc");
5046
5047     ts = cmd_CreateSyntax("setclientaddrs", SetClientAddrsCmd, NULL,
5048                           "set client network interface addresses");
5049     cmd_AddParm(ts, "-address", CMD_LIST, CMD_OPTIONAL | CMD_EXPANDS,
5050                 "client network interfaces");
5051     cmd_CreateAlias(ts, "sc");
5052 #endif /* WIN32 */
5053
5054     ts = cmd_CreateSyntax("setserverprefs", SetPrefCmd, NULL, "set server ranks");
5055     cmd_AddParm(ts, "-servers", CMD_LIST, CMD_OPTIONAL|CMD_EXPANDS, "fileserver names and ranks");
5056     cmd_AddParm(ts, "-vlservers", CMD_LIST, CMD_OPTIONAL|CMD_EXPANDS, "VL server names and ranks");
5057     cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_OPTIONAL, "input from named file");
5058     cmd_AddParm(ts, "-stdin", CMD_FLAG, CMD_OPTIONAL, "input from stdin");
5059     cmd_CreateAlias(ts, "sp");
5060
5061     ts = cmd_CreateSyntax("getserverprefs", GetPrefCmd, NULL, "get server ranks");
5062     cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_OPTIONAL, "output to named file");
5063     cmd_AddParm(ts, "-numeric", CMD_FLAG, CMD_OPTIONAL, "addresses only");
5064     cmd_AddParm(ts, "-vlservers", CMD_FLAG, CMD_OPTIONAL, "VL servers");
5065     /* cmd_AddParm(ts, "-cell", CMD_FLAG, CMD_OPTIONAL, "cellname"); */
5066     cmd_CreateAlias(ts, "gp");
5067
5068     ts = cmd_CreateSyntax("setacl", SetACLCmd, NULL, "set access control list");
5069     cmd_AddParm(ts, "-dir", CMD_LIST, 0, "directory");
5070     cmd_AddParm(ts, "-acl", CMD_LIST, 0, "access list entries");
5071     cmd_AddParm(ts, "-clear", CMD_FLAG, CMD_OPTIONAL, "clear access list");
5072     cmd_AddParm(ts, "-negative", CMD_FLAG, CMD_OPTIONAL, "apply to negative rights");
5073     parm_setacl_id = ts->nParms;
5074     cmd_AddParm(ts, "-id", CMD_FLAG, CMD_OPTIONAL, "initial directory acl (DFS only)");
5075     cmd_AddParm(ts, "-if", CMD_FLAG, CMD_OPTIONAL, "initial file acl (DFS only)");
5076     cmd_CreateAlias(ts, "sa");
5077     
5078     ts = cmd_CreateSyntax("listacl", ListACLCmd, NULL, "list access control list");
5079     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
5080     parm_listacl_id = ts->nParms;
5081     cmd_AddParm(ts, "-id", CMD_FLAG, CMD_OPTIONAL, "initial directory acl");
5082     cmd_AddParm(ts, "-if", CMD_FLAG, CMD_OPTIONAL, "initial file acl");
5083     cmd_AddParm(ts, "-cmd", CMD_FLAG, CMD_OPTIONAL, "output as 'fs setacl' command");
5084     cmd_CreateAlias(ts, "la");
5085     
5086     ts = cmd_CreateSyntax("cleanacl", CleanACLCmd, NULL, "clean up access control list");
5087     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
5088     
5089     ts = cmd_CreateSyntax("copyacl", CopyACLCmd, NULL, "copy access control list");
5090     cmd_AddParm(ts, "-fromdir", CMD_SINGLE, 0, "source directory (or DFS file)");
5091     cmd_AddParm(ts, "-todir", CMD_LIST, 0, "destination directory (or DFS file)");
5092     cmd_AddParm(ts, "-clear", CMD_FLAG, CMD_OPTIONAL, "first clear dest access list");
5093     parm_copyacl_id = ts->nParms;
5094     cmd_AddParm(ts, "-id", CMD_FLAG, CMD_OPTIONAL, "initial directory acl");
5095     cmd_AddParm(ts, "-if", CMD_FLAG, CMD_OPTIONAL, "initial file acl");
5096     
5097     cmd_CreateAlias(ts, "ca");
5098
5099     ts = cmd_CreateSyntax("flush", FlushCmd, NULL, "flush file from cache");
5100     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
5101     cmd_AddParm(ts, "-literal", CMD_FLAG, CMD_OPTIONAL, "literal evaluation of mountpoints and symlinks");
5102     
5103 #ifndef WIN32
5104     ts = cmd_CreateSyntax("flushmount", FlushMountCmd, NULL,
5105                            "flush mount symlink from cache");
5106     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
5107 #endif
5108
5109     ts = cmd_CreateSyntax("setvol", SetVolCmd, NULL, "set volume status");
5110     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
5111     cmd_AddParm(ts, "-max", CMD_SINGLE, CMD_OPTIONAL, "disk space quota in 1K units");
5112 #ifdef notdef
5113     cmd_AddParm(ts, "-min", CMD_SINGLE, CMD_OPTIONAL, "disk space guaranteed");
5114 #endif
5115     cmd_AddParm(ts, "-motd", CMD_SINGLE, CMD_OPTIONAL, "message of the day");
5116     cmd_AddParm(ts, "-offlinemsg", CMD_SINGLE, CMD_OPTIONAL, "offline message");
5117     cmd_CreateAlias(ts, "sv");
5118     
5119     ts = cmd_CreateSyntax("messages", MessagesCmd, NULL, "control Cache Manager messages");
5120     cmd_AddParm(ts, "-show", CMD_SINGLE, CMD_OPTIONAL, "[user|console|all|none]");
5121
5122     ts = cmd_CreateSyntax("examine", ExamineCmd, NULL, "display file/volume status");
5123     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
5124     cmd_AddParm(ts, "-literal", CMD_FLAG, CMD_OPTIONAL, "literal evaluation of mountpoints and symlinks");
5125     cmd_CreateAlias(ts, "lv");
5126     cmd_CreateAlias(ts, "listvol");
5127     
5128     ts = cmd_CreateSyntax("listquota", ListQuotaCmd, NULL, "list volume quota");
5129     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
5130     cmd_CreateAlias(ts, "lq");
5131     
5132     ts = cmd_CreateSyntax("diskfree", DiskFreeCmd, NULL, "show server disk space usage");
5133     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
5134     cmd_CreateAlias(ts, "df");
5135     
5136     ts = cmd_CreateSyntax("quota", QuotaCmd, NULL, "show volume quota usage");
5137     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
5138     
5139     ts = cmd_CreateSyntax("lsmount", ListMountCmd, NULL, "list mount point");    
5140     cmd_AddParm(ts, "-dir", CMD_LIST, 0, "directory");
5141     
5142     ts = cmd_CreateSyntax("mkmount", MakeMountCmd, NULL, "make mount point");
5143     cmd_AddParm(ts, "-dir", CMD_SINGLE, 0, "directory");
5144     cmd_AddParm(ts, "-vol", CMD_SINGLE, 0, "volume name");
5145     cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell name");
5146     cmd_AddParm(ts, "-rw", CMD_FLAG, CMD_OPTIONAL, "force r/w volume");
5147     cmd_AddParm(ts, "-fast", CMD_FLAG, CMD_OPTIONAL, "don't check name with VLDB");
5148
5149     /*
5150      *
5151      * defect 3069
5152      * 
5153     cmd_AddParm(ts, "-root", CMD_FLAG, CMD_OPTIONAL, "create cellular mount point");
5154     */
5155
5156     
5157     ts = cmd_CreateSyntax("rmmount", RemoveMountCmd, NULL, "remove mount point");
5158     cmd_AddParm(ts, "-dir", CMD_LIST, 0, "directory");
5159     
5160     ts = cmd_CreateSyntax("checkservers", CheckServersCmd, NULL, "check local cell's servers");
5161     cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell to check");
5162     cmd_AddParm(ts, "-all", CMD_FLAG, CMD_OPTIONAL, "check all cells");
5163     cmd_AddParm(ts, "-fast", CMD_FLAG, CMD_OPTIONAL, "just list, don't check");
5164     cmd_AddParm(ts,"-interval",CMD_SINGLE,CMD_OPTIONAL,"seconds between probes");
5165     
5166     ts = cmd_CreateSyntax("checkvolumes", CheckVolumesCmd, NULL, "check volumeID/name mappings");
5167     cmd_CreateAlias(ts, "checkbackups");
5168
5169     
5170     ts = cmd_CreateSyntax("setcachesize", SetCacheSizeCmd, NULL, "set cache size");
5171     cmd_AddParm(ts, "-blocks", CMD_SINGLE, CMD_OPTIONAL, "size in 1K byte blocks (0 => reset)");
5172     cmd_CreateAlias(ts, "cachesize");
5173
5174     cmd_AddParm(ts, "-reset", CMD_FLAG, CMD_OPTIONAL, "reset size back to boot value");
5175     
5176     ts = cmd_CreateSyntax("getcacheparms", GetCacheParmsCmd, NULL, "get cache usage info");
5177
5178     ts = cmd_CreateSyntax("listcells", ListCellsCmd, NULL, "list configured cells");
5179     cmd_AddParm(ts, "-numeric", CMD_FLAG, CMD_OPTIONAL, "addresses only");
5180     
5181     ts = cmd_CreateSyntax("setquota", SetQuotaCmd, NULL, "set volume quota");
5182     cmd_AddParm(ts, "-path", CMD_SINGLE, CMD_OPTIONAL, "dir/file path");
5183     cmd_AddParm(ts, "-max", CMD_SINGLE, 0, "max quota in kbytes");
5184 #ifdef notdef
5185     cmd_AddParm(ts, "-min", CMD_SINGLE, CMD_OPTIONAL, "min quota in kbytes");
5186 #endif
5187     cmd_CreateAlias(ts, "sq");
5188
5189     ts = cmd_CreateSyntax("newcell", NewCellCmd, NULL, "configure new cell");
5190 #ifndef WIN32
5191     cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "cell name");
5192     cmd_AddParm(ts, "-servers", CMD_LIST, CMD_REQUIRED, "primary servers");
5193     cmd_AddParm(ts, "-linkedcell", CMD_SINGLE, CMD_OPTIONAL, "linked cell name");
5194
5195 #ifdef FS_ENABLE_SERVER_DEBUG_PORTS
5196     /*
5197      * Turn this on only if you wish to be able to talk to a server which is listening
5198      * on alternative ports. This is not intended for general use and may not be
5199      * supported in the cache manager. It is not a way to run two servers at the
5200      * same host, since the cache manager cannot properly distinguish those two hosts.
5201      */
5202     cmd_AddParm(ts, "-fsport", CMD_SINGLE, CMD_OPTIONAL, "cell's fileserver port");
5203     cmd_AddParm(ts, "-vlport", CMD_SINGLE, CMD_OPTIONAL, "cell's vldb server port");
5204 #endif
5205
5206     ts = cmd_CreateSyntax("newalias", NewAliasCmd, NULL,
5207                           "configure new cell alias");
5208     cmd_AddParm(ts, "-alias", CMD_SINGLE, 0, "alias name");
5209     cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "real name of cell");
5210 #endif
5211
5212     ts = cmd_CreateSyntax("whichcell", WhichCellCmd, NULL, "list file's cell");
5213     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
5214     cmd_AddParm(ts, "-literal", CMD_FLAG, CMD_OPTIONAL, "literal evaluation of mountpoints and symlinks");
5215
5216     ts = cmd_CreateSyntax("whereis", WhereIsCmd, NULL, "list file's location");
5217     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
5218     cmd_AddParm(ts, "-literal", CMD_FLAG, CMD_OPTIONAL, "literal evaluation of mountpoints and symlinks");
5219
5220     ts = cmd_CreateSyntax("wscell", WSCellCmd, NULL, "list workstation's cell");
5221     
5222     /*
5223      ts = cmd_CreateSyntax("primarycell", PrimaryCellCmd, 0, "obsolete (listed primary cell)");
5224      */
5225     
5226 #ifndef AFS_NT40_ENV
5227     ts = cmd_CreateSyntax("monitor", MonitorCmd, NULL, "set cache monitor host address");
5228     cmd_AddParm(ts, "-server", CMD_SINGLE, CMD_OPTIONAL, "host name or 'off'");
5229     cmd_CreateAlias(ts, "mariner");
5230 #endif
5231
5232     ts = cmd_CreateSyntax("getcellstatus", GetCellCmd, NULL, "get cell status");
5233     cmd_AddParm(ts, "-cell", CMD_LIST, 0, "cell name");
5234     
5235     ts = cmd_CreateSyntax("setcell", SetCellCmd, NULL, "set cell status");
5236     cmd_AddParm(ts, "-cell", CMD_LIST, 0, "cell name");
5237     cmd_AddParm(ts, "-suid", CMD_FLAG, CMD_OPTIONAL, "allow setuid programs");
5238     cmd_AddParm(ts, "-nosuid", CMD_FLAG, CMD_OPTIONAL, "disallow setuid programs");
5239
5240     ts = cmd_CreateSyntax("flushall", FlushAllCmd, NULL, "flush all data");
5241
5242     ts = cmd_CreateSyntax("flushvolume", FlushVolumeCmd, NULL, "flush all data in volume");
5243     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
5244
5245     ts = cmd_CreateSyntax("sysname", SysNameCmd, NULL, "get/set sysname (i.e. @sys) value");
5246     cmd_AddParm(ts, "-newsys", CMD_LIST, CMD_OPTIONAL, "new sysname");
5247
5248 #ifndef AFS_NT40_ENV
5249     ts = cmd_CreateSyntax("exportafs", ExportAfsCmd, NULL, "enable/disable translators to AFS");
5250     cmd_AddParm(ts, "-type", CMD_SINGLE, 0, "exporter name");
5251     cmd_AddParm(ts, "-start", CMD_SINGLE, CMD_OPTIONAL, "start/stop translator ('on' or 'off')");
5252     cmd_AddParm(ts, "-convert", CMD_SINGLE, CMD_OPTIONAL, "convert from afs to unix mode ('on or 'off')");
5253     cmd_AddParm(ts, "-uidcheck", CMD_SINGLE, CMD_OPTIONAL, "run on strict 'uid check' mode ('on' or 'off')");
5254     cmd_AddParm(ts, "-submounts", CMD_SINGLE, CMD_OPTIONAL, "allow nfs mounts to subdirs of /afs/.. ('on' or 'off')");
5255 #endif
5256
5257     ts = cmd_CreateSyntax("storebehind", StoreBehindCmd, NULL, 
5258                           "store to server after file close");
5259     cmd_AddParm(ts, "-kbytes", CMD_SINGLE, CMD_OPTIONAL, "asynchrony for specified names");
5260     cmd_AddParm(ts, "-files", CMD_LIST, CMD_OPTIONAL, "specific pathnames");
5261     cmd_AddParm(ts, "-allfiles", CMD_SINGLE, CMD_OPTIONAL, "new default (KB)");
5262     cmd_CreateAlias(ts, "sb");
5263
5264     ts = cmd_CreateSyntax("setcrypt", SetCryptCmd, NULL, "set cache manager encryption flag");
5265     cmd_AddParm(ts, "-crypt", CMD_SINGLE, 0, "on or off");
5266
5267     ts = cmd_CreateSyntax("getcrypt", GetCryptCmd, NULL, "get cache manager encryption flag");
5268
5269     ts = cmd_CreateSyntax("rxstatproc", RxStatProcCmd, NULL,
5270                           "Manage per process RX statistics");
5271     cmd_AddParm(ts, "-enable", CMD_FLAG, CMD_OPTIONAL, "Enable RX stats");
5272     cmd_AddParm(ts, "-disable", CMD_FLAG, CMD_OPTIONAL, "Disable RX stats");
5273     cmd_AddParm(ts, "-clear", CMD_FLAG, CMD_OPTIONAL, "Clear RX stats");
5274
5275     ts = cmd_CreateSyntax("rxstatpeer", RxStatPeerCmd, NULL,
5276                           "Manage per peer RX statistics");
5277     cmd_AddParm(ts, "-enable", CMD_FLAG, CMD_OPTIONAL, "Enable RX stats");
5278     cmd_AddParm(ts, "-disable", CMD_FLAG, CMD_OPTIONAL, "Disable RX stats");
5279     cmd_AddParm(ts, "-clear", CMD_FLAG, CMD_OPTIONAL, "Clear RX stats");
5280
5281 #ifndef WIN32
5282     ts = cmd_CreateSyntax("setcbaddr", CallBackRxConnCmd, NULL, "configure callback connection address");
5283     cmd_AddParm(ts, "-addr", CMD_SINGLE, CMD_OPTIONAL, "host name or address");
5284 #endif
5285
5286     ts = cmd_CreateSyntax("trace", TraceCmd, NULL, "enable or disable CM tracing");
5287     cmd_AddParm(ts, "-on", CMD_FLAG, CMD_OPTIONAL, "enable tracing");
5288     cmd_AddParm(ts, "-off", CMD_FLAG, CMD_OPTIONAL, "disable tracing");
5289     cmd_AddParm(ts, "-reset", CMD_FLAG, CMD_OPTIONAL, "reset log contents");
5290     cmd_AddParm(ts, "-dump", CMD_FLAG, CMD_OPTIONAL, "dump log contents");
5291     cmd_CreateAlias(ts, "tr");
5292
5293     ts = cmd_CreateSyntax("uuid", UuidCmd, NULL, "manage the UUID for the cache manager");
5294     cmd_AddParm(ts, "-generate", CMD_FLAG, CMD_OPTIONAL, "generate a new UUID");
5295
5296     ts = cmd_CreateSyntax("memdump", MemDumpCmd, NULL, "dump memory allocs in debug builds");
5297     cmd_AddParm(ts, "-begin", CMD_FLAG, CMD_OPTIONAL, "set a memory checkpoint");
5298     cmd_AddParm(ts, "-end", CMD_FLAG, CMD_OPTIONAL, "dump memory allocs");
5299     
5300     ts = cmd_CreateSyntax("cscpolicy", CSCPolicyCmd, NULL, "change client side caching policy for AFS shares");
5301     cmd_AddParm(ts, "-share", CMD_SINGLE, CMD_OPTIONAL, "AFS share");
5302     cmd_AddParm(ts, "-manual", CMD_FLAG, CMD_OPTIONAL, "manual caching of documents");
5303     cmd_AddParm(ts, "-programs", CMD_FLAG, CMD_OPTIONAL, "automatic caching of programs and documents");
5304     cmd_AddParm(ts, "-documents", CMD_FLAG, CMD_OPTIONAL, "automatic caching of documents");
5305     cmd_AddParm(ts, "-disable", CMD_FLAG, CMD_OPTIONAL, "disable caching");
5306
5307     ts = cmd_CreateSyntax("minidump", MiniDumpCmd, NULL, "Generate MiniDump of current service state");
5308
5309     ts = cmd_CreateSyntax("test_volstat", TestVolStatCmd, NULL, (char *)CMD_HIDDEN);
5310     cmd_AddParm(ts, "-network", CMD_SINGLE, CMD_OPTIONAL, "set network state up or down");
5311     cmd_AddParm(ts, "-check",   CMD_FLAG,   CMD_OPTIONAL, "check state of offline volumes");
5312     cmd_AddParm(ts, "-cell",    CMD_SINGLE, CMD_OPTIONAL, "cell name or number");
5313     cmd_AddParm(ts, "-volume",  CMD_SINGLE, CMD_OPTIONAL, "volume name or number");
5314     cmd_AddParm(ts, "-state",   CMD_SINGLE, CMD_OPTIONAL, "new volume state: online, busy, offline, down");
5315
5316     ts = cmd_CreateSyntax("smbunicode", SmbUnicodeCmd, NULL, "enable or disable Unicode on new SMB connections");
5317     cmd_AddParm(ts, "-on", CMD_FLAG, CMD_OPTIONAL, "enable Unicode on new connections");
5318     cmd_AddParm(ts, "-off", CMD_FLAG, CMD_OPTIONAL, "disable Unicode on new connections");
5319
5320     ts = cmd_CreateSyntax("getfid", GetFidCmd, NULL, "get file id for object(s) in afs");
5321     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
5322     cmd_AddParm(ts, "-literal", CMD_FLAG, CMD_OPTIONAL, "literal evaluation of mountpoints and symlinks");
5323
5324     ts = cmd_CreateSyntax("chown", ChOwnCmd, NULL, "set owner for object(s) in afs");
5325     cmd_AddParm(ts, "-owner", CMD_SINGLE, 0, "user name or id");
5326     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
5327     cmd_AddParm(ts, "-literal", CMD_FLAG, CMD_OPTIONAL, "literal evaluation of mountpoints and symlinks");
5328
5329     ts = cmd_CreateSyntax("chgrp", ChGrpCmd, NULL, "set owner for object(s) in afs");
5330     cmd_AddParm(ts, "-group", CMD_SINGLE, 0, "user/group name or id");
5331     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
5332     cmd_AddParm(ts, "-literal", CMD_FLAG, CMD_OPTIONAL, "literal evaluation of mountpoints and symlinks");
5333
5334     code = cmd_Dispatch(argc, argv);
5335
5336     if (rxInitDone) 
5337         rx_Finalize();
5338     
5339     FreeUtf8CmdLine(argc, argv);
5340     
5341     return code;
5342 }
5343
5344 static void 
5345 Die(int code, char *filename)
5346 { /*Die*/
5347
5348     if (code == EINVAL) {
5349         if (filename)
5350             fprintf(stderr,"%s: Invalid argument; it is possible that %s is not in AFS.\n", pn, filename);
5351         else 
5352             fprintf(stderr,"%s: Invalid argument.\n", pn);
5353     }
5354     else if (code == ENOENT) {
5355         if (filename) 
5356             fprintf(stderr,"%s: File '%s' doesn't exist\n", pn, filename);
5357         else 
5358             fprintf(stderr,"%s: no such file returned\n", pn);
5359     }
5360     else if (code == EROFS)  
5361         fprintf(stderr,"%s: You can not change a backup or readonly volume\n", pn);
5362     else if (code == EACCES || code == EPERM) {
5363         if (filename) 
5364             fprintf(stderr,"%s: You don't have the required access rights on '%s'\n", pn, filename);
5365         else 
5366             fprintf(stderr,"%s: You do not have the required rights to do this operation\n", pn);
5367     }
5368     else if (code == ENODEV) {
5369         fprintf(stderr,"%s: AFS service may not have started.\n", pn);
5370     }
5371     else if (code == ESRCH) {   /* hack */
5372         fprintf(stderr,"%s: Cell name not recognized.\n", pn);
5373     }
5374     else if (code == EPIPE) {   /* hack */
5375         fprintf(stderr,"%s: Volume name or ID not recognized.\n", pn);
5376     }
5377     else if (code == EFBIG) {
5378         fprintf(stderr,"%s: Cache size too large.\n", pn);
5379     }
5380     else if (code == ETIMEDOUT) {
5381         if (filename)
5382             fprintf(stderr,"%s:'%s': Connection timed out", pn, filename);
5383         else
5384             fprintf(stderr,"%s: Connection timed out", pn);
5385     }
5386     else if (code == EBUSY) {
5387         if (filename) 
5388             fprintf(stderr,"%s: All servers are busy on which '%s' resides\n", pn, filename);
5389         else 
5390             fprintf(stderr,"%s: All servers are busy\n", pn);
5391     } 
5392     else if (code == ENXIO) {
5393         if (filename) 
5394             fprintf(stderr,"%s: All volume instances are offline on which '%s' resides\n", pn, filename);
5395         else 
5396             fprintf(stderr,"%s: All volume instances are offline\n", pn);
5397     } 
5398     else if (code == ENOSYS) {
5399         if (filename) 
5400             fprintf(stderr,"%s: All servers are down on which '%s' resides\n", pn, filename);
5401         else 
5402             fprintf(stderr,"%s: All servers are down\n", pn);
5403     } 
5404     else if (code == ECHILD) {  /* hack */
5405         if (filename) 
5406             fprintf(stderr,"%s: Invalid owner specified for '%s'\n", pn, filename);
5407         else 
5408             fprintf(stderr,"%s: Invalid owner specified\n", pn);
5409     } 
5410     else {
5411         if (filename) 
5412             fprintf(stderr,"%s:'%s'", pn, filename);
5413         else 
5414             fprintf(stderr,"%s", pn);
5415 #ifdef WIN32
5416         fprintf(stderr, ": code 0x%x\n", code);
5417 #else /* not WIN32 */
5418         fprintf(stderr,": %s\n", afs_error_message(code));
5419 #endif /* not WIN32 */
5420     }
5421 } /*Die*/
5422