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