windows-fs-getfid-20090511
[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_stricmp_utf8N("Freelance.Local.Root",space);
283     return 1;   /* assume it is because it is more restrictive that way */
284 }
285
286 /* return a static pointer to a buffer */
287 static char *
288 Parent(char *apath)
289 {
290     char *tp;
291     strcpy(tspace, apath);
292     tp = strrchr(tspace, '\\');
293     if (tp) {
294         *(tp+1) = 0;    /* lv trailing slash so Parent("k:\foo") is "k:\" not "k:" */
295     }
296     else {
297         fs_ExtractDriveLetter(apath, tspace);
298         strcat(tspace, ".");
299     }
300     return tspace;
301 }
302
303 enum rtype {add, destroy, deny};
304
305 static afs_int32 
306 Convert(char *arights, int dfs, enum rtype *rtypep)
307 {
308     int i, len;
309     afs_int32 mode;
310     char tc;
311
312     *rtypep = add;      /* add rights, by default */
313
314     if (dfs) {
315         if (!strcmp(arights, "null")) {
316             *rtypep = deny;
317             return 0;
318         }
319         if (!strcmp(arights,"read")) 
320             return DFS_READ | DFS_EXECUTE;
321         if (!strcmp(arights, "write")) 
322             return DFS_READ | DFS_EXECUTE | DFS_INSERT | DFS_DELETE | 
323                 DFS_WRITE;
324         if (!strcmp(arights, "all")) 
325             return DFS_READ | DFS_EXECUTE | DFS_INSERT | DFS_DELETE | 
326                 DFS_WRITE | DFS_CONTROL;
327     } else {
328         if (!strcmp(arights,"read")) 
329             return PRSFS_READ | PRSFS_LOOKUP;
330         if (!strcmp(arights, "write")) 
331             return PRSFS_READ | PRSFS_LOOKUP | PRSFS_INSERT | PRSFS_DELETE | 
332                 PRSFS_WRITE | PRSFS_LOCK;
333         if (!strcmp(arights, "mail")) 
334             return PRSFS_INSERT | PRSFS_LOCK | PRSFS_LOOKUP;
335         if (!strcmp(arights, "all")) 
336             return PRSFS_READ | PRSFS_LOOKUP | PRSFS_INSERT | PRSFS_DELETE | 
337                 PRSFS_WRITE | PRSFS_LOCK | PRSFS_ADMINISTER;
338     }
339     if (!strcmp(arights, "none")) {
340         *rtypep = destroy; /* Remove entire entry */
341         return 0;
342     }
343     len = (int)strlen(arights);
344     mode = 0;
345     for(i=0;i<len;i++) {
346         tc = *arights++;
347         if (dfs) {
348             if (tc == '-') 
349                 continue;
350             else if (tc == 'r') 
351                 mode |= DFS_READ;
352             else if (tc == 'w') 
353                 mode |= DFS_WRITE;
354             else if (tc == 'x') 
355                 mode |= DFS_EXECUTE;
356             else if (tc == 'c') 
357                 mode |= DFS_CONTROL;
358             else if (tc == 'i') 
359                 mode |= DFS_INSERT;
360             else if (tc == 'd') 
361                 mode |= DFS_DELETE;
362             else if (tc == 'A') 
363                 mode |= DFS_USR0;
364             else if (tc == 'B') 
365                 mode |= DFS_USR1;
366             else if (tc == 'C') 
367                 mode |= DFS_USR2;
368             else if (tc == 'D') 
369                 mode |= DFS_USR3;
370             else if (tc == 'E') 
371                 mode |= DFS_USR4;
372             else if (tc == 'F') 
373                 mode |= DFS_USR5;
374             else if (tc == 'G') 
375                 mode |= DFS_USR6;
376             else if (tc == 'H') 
377                 mode |= DFS_USR7;
378             else {
379                 fprintf(stderr, "%s: illegal DFS rights character '%c'.\n", 
380                          pn, tc);
381                 exit(1);
382             }
383         } else {
384             if (tc == 'r') 
385                 mode |= PRSFS_READ;
386             else if (tc == 'l') 
387                 mode |= PRSFS_LOOKUP;
388             else if (tc == 'i') 
389                 mode |= PRSFS_INSERT;
390             else if (tc == 'd') 
391                 mode |= PRSFS_DELETE;
392             else if (tc == 'w') 
393                 mode |= PRSFS_WRITE;
394             else if (tc == 'k') 
395                 mode |= PRSFS_LOCK;
396             else if (tc == 'a') 
397                 mode |= PRSFS_ADMINISTER;
398             else if (tc == 'A') 
399                 mode |= PRSFS_USR0;
400             else if (tc == 'B') 
401                 mode |= PRSFS_USR1;
402             else if (tc == 'C') 
403                 mode |= PRSFS_USR2;
404             else if (tc == 'D') 
405                 mode |= PRSFS_USR3;
406             else if (tc == 'E') 
407                 mode |= PRSFS_USR4;
408             else if (tc == 'F') 
409                 mode |= PRSFS_USR5;
410             else if (tc == 'G') 
411                 mode |= PRSFS_USR6;
412             else if (tc == 'H') 
413                 mode |= PRSFS_USR7;
414             else {
415                 fprintf(stderr, "%s: illegal rights character '%c'.\n", pn, 
416                          tc);
417                 exit(1);
418             }
419         }
420     }
421     return mode;
422 }
423
424 static struct AclEntry *
425 FindList (struct AclEntry *alist, char *aname)
426 {
427     while (alist) {
428         if (!strcasecmp(alist->name, aname)) 
429             return alist;
430         alist = alist->next;
431     }
432     return 0;
433 }
434
435 /* if no parm specified in a particular slot, set parm to be "." instead */
436 static void 
437 SetDotDefault(struct cmd_item **aitemp)
438 {
439     struct cmd_item *ti;
440     if (*aitemp) 
441         return;                 /* already has value */
442     /* otherwise, allocate an item representing "." */
443     ti = (struct cmd_item *) malloc(sizeof(struct cmd_item));
444     assert(ti);
445     ti->next = (struct cmd_item *) 0;
446     ti->data = (char *) malloc(2);
447     assert(ti->data);
448     strcpy(ti->data, ".");
449     *aitemp = ti;
450 }
451
452 static void 
453 ChangeList (struct Acl *al, afs_int32 plus, char *aname, afs_int32 arights)
454 {
455     struct AclEntry *tlist;
456     tlist = (plus ? al->pluslist : al->minuslist);
457     tlist = FindList (tlist, aname);
458     if (tlist) {
459         /* Found the item already in the list. */
460         tlist->rights = arights;
461         if (plus)
462             al->nplus -= PruneList(&al->pluslist, al->dfs);
463         else
464             al->nminus -= PruneList(&al->minuslist, al->dfs);
465         return;
466     }
467     /* Otherwise we make a new item and plug in the new data. */
468     tlist = (struct AclEntry *) malloc(sizeof (struct AclEntry));
469     assert(tlist);
470     strcpy(tlist->name, aname);
471     tlist->rights = arights;
472     if (plus) {
473         tlist->next = al->pluslist;
474         al->pluslist = tlist;
475         al->nplus++;
476         if (arights == 0 || arights == -1)
477             al->nplus -= PruneList(&al->pluslist, al->dfs);
478     } else {
479         tlist->next = al->minuslist;
480         al->minuslist = tlist;
481         al->nminus++;
482         if (arights == 0) 
483             al->nminus -= PruneList(&al->minuslist, al->dfs);
484     }
485 }
486
487 static void 
488 ZapList (struct AclEntry *alist)
489 {
490     struct AclEntry *tp, *np;
491     for (tp = alist; tp; tp = np) {
492         np = tp->next;
493         free(tp);
494     }
495 }
496
497 static int 
498 PruneList (struct AclEntry **ae, int dfs)
499 {
500     struct AclEntry **lp;
501     struct AclEntry *te, *ne;
502     afs_int32 ctr;
503     ctr = 0;
504     lp = ae;
505     for(te = *ae;te;te=ne) {
506         if ((!dfs && te->rights == 0) || te->rights == -1) {
507             *lp = te->next;
508             ne = te->next;
509             free(te);
510             ctr++;
511         } else {
512             ne = te->next;
513             lp = &te->next;
514         }
515     }
516     return ctr;
517 }
518
519 static char *
520 SkipLine (char *astr)
521 {
522     while (*astr !='\n') 
523         astr++;
524     astr++;
525     return astr;
526 }
527
528 /*
529  * Create an empty acl, taking into account whether the acl pointed
530  * to by astr is an AFS or DFS acl. Only parse this minimally, so we
531  * can recover from problems caused by bogus ACL's (in that case, always
532  * assume that the acl is AFS: for DFS, the user can always resort to
533  * acl_edit, but for AFS there may be no other way out).
534  */
535 static struct Acl *
536 EmptyAcl(char *astr)
537 {
538     struct Acl *tp;
539     int junk;
540
541     tp = (struct Acl *)malloc(sizeof (struct Acl));
542     assert(tp);
543     tp->nplus = tp->nminus = 0;
544     tp->pluslist = tp->minuslist = 0;
545     tp->dfs = 0;
546     if (astr == NULL || sscanf(astr, "%d dfs:%d %s", &junk, &tp->dfs, tp->cell) <= 0) {
547         tp->dfs = 0;
548         tp->cell[0] = '\0';
549     }
550     return tp;
551 }
552
553 static struct Acl *
554 ParseAcl (char *astr)
555 {
556     int nplus, nminus, i, trights, ret;
557     char tname[MAXNAME];
558     struct AclEntry *first, *next, *last, *tl;
559     struct Acl *ta;
560
561     ta = EmptyAcl(NULL);
562     if (astr == NULL || strlen(astr) == 0)
563         return ta;
564
565     ret = sscanf(astr, "%d dfs:%d %s", &ta->nplus, &ta->dfs, ta->cell);
566     if (ret <= 0) {
567         free(ta);
568         return NULL;
569     }
570     astr = SkipLine(astr);
571     ret = sscanf(astr, "%d", &ta->nminus);
572     if (ret <= 0) {
573         free(ta);
574         return NULL;
575     }
576     astr = SkipLine(astr);
577
578     nplus = ta->nplus;
579     nminus = ta->nminus;
580
581     last = 0;
582     first = 0;
583     for(i=0;i<nplus;i++) {
584         ret = sscanf(astr, "%100s %d", tname, &trights); 
585         if (ret <= 0)
586             goto nplus_err;
587         astr = SkipLine(astr);
588         tl = (struct AclEntry *) malloc(sizeof (struct AclEntry));
589         if (tl == NULL)
590             goto nplus_err;
591         if (!first) 
592             first = tl;
593         strcpy(tl->name, tname);
594         tl->rights = trights;
595         tl->next = 0;
596         if (last) 
597             last->next = tl;
598         last = tl;
599     }
600     ta->pluslist = first;
601
602     last = 0;
603     first = 0;
604     for(i=0;i<nminus;i++) {
605         ret = sscanf(astr, "%100s %d", tname, &trights);
606         if (ret <= 0)
607             goto nminus_err;
608         astr = SkipLine(astr);
609         tl = (struct AclEntry *) malloc(sizeof (struct AclEntry));
610         if (tl == NULL)
611             goto nminus_err;
612         if (!first) 
613             first = tl;
614         strcpy(tl->name, tname);
615         tl->rights = trights;
616         tl->next = 0;
617         if (last) 
618             last->next = tl;
619         last = tl;
620     }
621     ta->minuslist = first;
622
623     return ta;
624
625   nminus_err:
626     for (;first; first = next) {
627         next = first->next;
628         free(first);
629     }   
630     first = ta->pluslist;
631
632   nplus_err:
633     for (;first; first = next) {
634         next = first->next;
635         free(first);
636     }   
637     free(ta);
638     return NULL;
639 }
640
641 static int
642 PrintStatus(VolumeStatus *status, char *name, char *motd, char *offmsg)
643 {
644     printf("Volume status for vid = %u named %s is\n",status->Vid, name);
645     if (*offmsg != 0)
646         printf("Current offline message is %s\n",offmsg);
647     if (*motd != 0)
648         printf("Current message of the day is %s\n",motd);
649     printf("Current disk quota is ");
650     if (status->MaxQuota != 0) 
651         printf("%d\n", status->MaxQuota);
652     else 
653         printf("unlimited\n");
654     printf("Current blocks used are %d\n",status->BlocksInUse);
655     printf("The partition has %d blocks available out of %d\n",
656             status->PartBlocksAvail, status->PartMaxBlocks);
657     return 0;
658 }
659
660 static int
661 QuickPrintStatus(VolumeStatus *status, char *name)
662 {
663     double QuotaUsed =0.0;
664     double PartUsed =0.0;
665     int WARN = 0;
666     printf("%-25.25s",name);
667
668     if (status->MaxQuota != 0) {
669         printf("%10d%10d", status->MaxQuota, status->BlocksInUse);
670         QuotaUsed = ((((double)status->BlocksInUse)/status->MaxQuota) * 100.0);
671     } else {
672         printf("no limit%10d", status->BlocksInUse);
673     }
674     if (QuotaUsed > 90.0){
675         printf(" %5.0f%%<<", QuotaUsed);
676         WARN = 1;
677     } else 
678         printf(" %5.0f%%  ", QuotaUsed);
679     PartUsed = (100.0 - ((((double)status->PartBlocksAvail)/status->PartMaxBlocks) * 100.0));
680     if (PartUsed > 97.0){
681         printf(" %9.0f%%<<", PartUsed);
682         WARN = 1;
683     } else 
684         printf(" %9.0f%%  ", PartUsed);
685     if (WARN){
686         printf("  <<WARNING\n");
687     } else 
688         printf("\n");
689     return 0;
690 }
691
692 static int
693 QuickPrintSpace(VolumeStatus *status, char *name)
694 {
695     double PartUsed =0.0;
696     int WARN = 0;
697     printf("%-25.25s",name);
698
699     printf("%10d%10d%10d", status->PartMaxBlocks, status->PartMaxBlocks - status->PartBlocksAvail, status->PartBlocksAvail);
700         
701     PartUsed = (100.0 - ((((double)status->PartBlocksAvail)/status->PartMaxBlocks) * 100.0));
702     if (PartUsed > 90.0){
703         printf(" %4.0f%%<<", PartUsed);
704         WARN = 1;
705     } else 
706         printf(" %4.0f%%  ", PartUsed);
707     if (WARN){
708         printf("  <<WARNING\n");
709     } else 
710         printf("\n");
711     return 0;
712 }
713
714 static char *
715 AclToString(struct Acl *acl)
716 {
717     static char mydata[MAXSIZE];
718     char tstring[MAXSIZE];
719     char dfsstring[30];
720     struct AclEntry *tp;
721     
722     if (acl->dfs) 
723         sprintf(dfsstring, " dfs:%d %s", acl->dfs, acl->cell);
724     else 
725         dfsstring[0] = '\0';
726     sprintf(mydata, "%d%s\n%d\n", acl->nplus, dfsstring, acl->nminus);
727     for (tp = acl->pluslist;tp;tp=tp->next) {
728         sprintf(tstring, "%s %d\n", tp->name, tp->rights);
729         strcat(mydata, tstring);
730     }
731     for (tp = acl->minuslist;tp;tp=tp->next) {
732         sprintf(tstring, "%s %d\n", tp->name, tp->rights);
733         strcat(mydata, tstring);
734     }
735     return mydata;
736 }
737
738 static DWORD IsFreelance(void)
739 {
740     HKEY  parmKey;
741     DWORD code;
742     DWORD dummyLen;
743     DWORD enabled = 0;
744
745     code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY,
746                          0, (IsWow64()?KEY_WOW64_64KEY:0)|KEY_QUERY_VALUE, &parmKey);
747     if (code == ERROR_SUCCESS) {
748         dummyLen = sizeof(cm_freelanceEnabled);
749         code = RegQueryValueEx(parmKey, "FreelanceClient", NULL, NULL,
750                             (BYTE *) &enabled, &dummyLen);
751         RegCloseKey (parmKey);
752     }
753     return enabled;
754 }
755
756 static const char * NetbiosName(void)
757 {
758     static char buffer[1024] = "AFS";
759     HKEY  parmKey;
760     DWORD code;
761     DWORD dummyLen;
762     DWORD enabled = 0;
763
764     code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY,
765                          0, (IsWow64()?KEY_WOW64_64KEY:0)|KEY_QUERY_VALUE, &parmKey);
766     if (code == ERROR_SUCCESS) {
767         dummyLen = sizeof(buffer);
768         code = RegQueryValueEx(parmKey, "NetbiosName", NULL, NULL,
769                                buffer, &dummyLen);
770         RegCloseKey (parmKey);
771     } else {
772         strcpy(buffer, "AFS");
773     }
774     return buffer;
775 }
776
777 #define AFSCLIENT_ADMIN_GROUPNAME "AFS Client Admins"
778
779 static BOOL IsAdmin (void)
780 {
781     static BOOL fAdmin = FALSE;
782     static BOOL fTested = FALSE;
783
784     if (!fTested)
785     {
786         /* Obtain the SID for the AFS client admin group.  If the group does
787          * not exist, then assume we have AFS client admin privileges.
788          */
789         PSID psidAdmin = NULL;
790         DWORD dwSize, dwSize2;
791         char pszAdminGroup[ MAX_COMPUTERNAME_LENGTH + sizeof(AFSCLIENT_ADMIN_GROUPNAME) + 2 ];
792         char *pszRefDomain = NULL;
793         SID_NAME_USE snu = SidTypeGroup;
794
795         dwSize = sizeof(pszAdminGroup);
796
797         if (!GetComputerName(pszAdminGroup, &dwSize)) {
798             /* Can't get computer name.  We return false in this case.
799                Retain fAdmin and fTested. This shouldn't happen.*/
800             return FALSE;
801         }
802
803         dwSize = 0;
804         dwSize2 = 0;
805
806         strcat(pszAdminGroup,"\\");
807         strcat(pszAdminGroup, AFSCLIENT_ADMIN_GROUPNAME);
808
809         LookupAccountName(NULL, pszAdminGroup, NULL, &dwSize, NULL, &dwSize2, &snu);
810         /* that should always fail. */
811
812         if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
813             /* if we can't find the group, then we allow the operation */
814             fAdmin = TRUE;
815             return TRUE;
816         }
817
818         if (dwSize == 0 || dwSize2 == 0) {
819             /* Paranoia */
820             fAdmin = TRUE;
821             return TRUE;
822         }
823
824         psidAdmin = (PSID)malloc(dwSize); memset(psidAdmin,0,dwSize);
825         assert(psidAdmin);
826         pszRefDomain = (char *)malloc(dwSize2);
827         assert(pszRefDomain);
828
829         if (!LookupAccountName(NULL, pszAdminGroup, psidAdmin, &dwSize, pszRefDomain, &dwSize2, &snu)) {
830             /* We can't lookup the group now even though we looked it up earlier.  
831                Could this happen? */
832             fAdmin = TRUE;
833         } else {
834             /* Then open our current ProcessToken */
835             HANDLE hToken;
836
837             if (OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken))
838             {
839
840                 if (!CheckTokenMembership(hToken, psidAdmin, &fAdmin)) {
841                     /* We'll have to allocate a chunk of memory to store the list of
842                      * groups to which this user belongs; find out how much memory
843                      * we'll need.
844                      */
845                     DWORD dwSize = 0;
846                     PTOKEN_GROUPS pGroups;
847
848                     GetTokenInformation (hToken, TokenGroups, NULL, dwSize, &dwSize);
849
850                     pGroups = (PTOKEN_GROUPS)malloc(dwSize);
851                     assert(pGroups);
852
853                     /* Allocate that buffer, and read in the list of groups. */
854                     if (GetTokenInformation (hToken, TokenGroups, pGroups, dwSize, &dwSize))
855                     {
856                         /* Look through the list of group SIDs and see if any of them
857                          * matches the AFS Client Admin group SID.
858                          */
859                         size_t iGroup = 0;
860                         for (; (!fAdmin) && (iGroup < pGroups->GroupCount); ++iGroup)
861                         {
862                             if (EqualSid (psidAdmin, pGroups->Groups[ iGroup ].Sid)) {
863                                 fAdmin = TRUE;
864                             }
865                         }
866                     }
867
868                     if (pGroups)
869                         free(pGroups);
870                 }
871
872                 /* if do not have permission because we were not explicitly listed
873                  * in the Admin Client Group let's see if we are the SYSTEM account
874                  */
875                 if (!fAdmin) {
876                     PTOKEN_USER pTokenUser;
877                     SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
878                     PSID pSidLocalSystem = 0;
879                     DWORD gle;
880
881                     GetTokenInformation(hToken, TokenUser, NULL, 0, &dwSize);
882
883                     pTokenUser = (PTOKEN_USER)malloc(dwSize);
884                     assert(pTokenUser);
885
886                     if (!GetTokenInformation(hToken, TokenUser, pTokenUser, dwSize, &dwSize))
887                         gle = GetLastError();
888
889                     if (AllocateAndInitializeSid( &SIDAuth, 1,
890                                                   SECURITY_LOCAL_SYSTEM_RID,
891                                                   0, 0, 0, 0, 0, 0, 0,
892                                                   &pSidLocalSystem))
893                     {
894                         if (EqualSid(pTokenUser->User.Sid, pSidLocalSystem)) {
895                             fAdmin = TRUE;
896                         }
897
898                         FreeSid(pSidLocalSystem);
899                     }
900
901                     if ( pTokenUser )
902                         free(pTokenUser);
903                 }
904             }
905         }
906
907         free(psidAdmin);
908         free(pszRefDomain);
909
910         fTested = TRUE;
911     }
912
913     return fAdmin;
914 }
915
916 static int
917 SetACLCmd(struct cmd_syndesc *as, void *arock)
918 {
919     afs_int32 code;
920     struct ViceIoctl blob;
921     struct Acl *ta = 0;
922     struct cmd_item *ti, *ui;
923     int plusp;
924     afs_int32 rights;
925     int clear;
926     int idf = getidf(as, parm_setacl_id);
927
928     int error = 0;
929
930     if (as->parms[2].items)
931         clear = 1;
932     else
933         clear = 0;
934     plusp = !(as->parms[3].items);
935     for(ti=as->parms[0].items; ti;ti=ti->next) {
936         blob.out_size = MAXSIZE;
937         blob.in_size = idf;
938         blob.in = blob.out = space;
939         code = pioctl_utf8(ti->data, VIOCGETAL, &blob, 1);
940         if (code) {
941             Die(errno, ti->data);
942             error = 1;
943             continue;
944         }
945         if (ta)
946             ZapAcl(ta);
947         ta = ParseAcl(space);
948         if (!ta) {
949             fprintf(stderr,
950                     "fs: %s: invalid acl data returned from VIOCGETAL\n",
951                      ti->data);
952             error = 1;
953             continue;
954         }
955         if (!plusp && ta->dfs) {
956             fprintf(stderr,
957                     "fs: %s: you may not use the -negative switch with DFS acl's.\n%s",
958                     ti->data,
959                     "(you may specify \"null\" to revoke all rights, however)\n");
960             error = 1;
961             continue;
962         }
963         if (ta)
964             ZapAcl(ta);
965         if (clear) 
966             ta = EmptyAcl(space);
967         else 
968             ta = ParseAcl(space);
969         if (!ta) {
970             fprintf(stderr,
971                     "fs: %s: invalid acl data returned from VIOCGETAL\n",
972                      ti->data);
973             error = 1;
974             continue;
975         }
976         CleanAcl(ta, ti->data);
977         for(ui=as->parms[1].items; ui; ui=ui->next->next) {
978             enum rtype rtype;
979             if (!ui->next) {
980                 fprintf(stderr,
981                         "%s: Missing second half of user/access pair.\n", pn);
982                 ZapAcl(ta);
983                 return 1;
984             }
985             rights = Convert(ui->next->data, ta->dfs, &rtype);
986             if (rtype == destroy && !ta->dfs) {
987                 struct AclEntry *tlist;
988
989                 tlist = (plusp ? ta->pluslist : ta->minuslist);
990                 if (!FindList(tlist, ui->data))
991                     continue;
992             }
993             if (rtype == deny && !ta->dfs) 
994                 plusp = 0;
995             if (rtype == destroy && ta->dfs) 
996                 rights = -1;
997             ChangeList(ta, plusp, ui->data, rights);
998         }
999         blob.in = AclToString(ta);
1000         blob.out_size=0;
1001         blob.in_size = 1+(long)strlen(blob.in);
1002         code = pioctl_utf8(ti->data, VIOCSETAL, &blob, 1);
1003         if (code) {
1004             if (errno == EINVAL) {
1005                 if (ta->dfs) {
1006                     static char *fsenv = 0;
1007                     if (!fsenv) {
1008                         fsenv = (char *)getenv("FS_EXPERT");
1009                     }
1010                     fprintf(stderr, "fs: \"Invalid argument\" was returned when you tried to store a DFS access list.\n");
1011                     if (!fsenv) {
1012                         fprintf(stderr,
1013     "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
1014     "\nPossible reasons for this include:\n\n",                     
1015     " -You may have specified an inappropriate combination of rights.\n",
1016     "  For example, some DFS-supported filesystems may not allow you to\n",
1017     "  drop the \"c\" right from \"user_obj\".\n\n",
1018     " -A mask_obj may be required (it is likely required by the underlying\n",
1019     "  filesystem if you try to set anything other than the basic \"user_obj\"\n",
1020     "  \"mask_obj\", or \"group_obj\" entries). Unlike acl_edit, the fs command\n",
1021     "  does not automatically create or update the mask_obj. Try setting\n",
1022     "  the rights \"mask_obj all\" with \"fs sa\" before adding any explicit\n",
1023     "  users or groups. You can do this with a single command, such as\n",
1024     "  \"fs sa mask_obj all user:somename read\"\n\n",
1025     " -A specified user or group may not exist.\n\n",
1026     " -You may have tried to delete \"user_obj\", \"group_obj\", or \"other_obj\".\n",
1027     "  This is probably not allowed by the underlying file system.\n\n",
1028     " -If you add a user or group to a DFS ACL, remember that it must be\n",
1029     "  fully specified as \"user:username\" or \"group:groupname\". In addition, there\n",
1030     "  may be local requirements on the format of the user or group name.\n",
1031     "  Check with your cell administrator.\n\n",                            
1032     " -Or numerous other possibilities. It would be great if we could be more\n",
1033     "  precise about the actual problem, but for various reasons, this is\n",
1034     "  impractical via this interface.  If you can't figure it out, you\n",
1035     "  might try logging into a DCE-equipped machine and use acl_edit (or\n",
1036     "  whatever is provided). You may get better results. Good luck!\n\n",
1037     " (You may inhibit this message by setting \"FS_EXPERT\" in your environment)\n");
1038                     }
1039                 } else {
1040                     fprintf(stderr,
1041                             "%s: Invalid argument, possible reasons include:\n", 
1042                              pn);
1043                     fprintf(stderr,"\t-File not in AFS\n");
1044                     fprintf(stderr,
1045                             "\t-Too many users on access control list\n");
1046                     fprintf(stderr,
1047                             "\t-Tried to add non-existent user to access control list\n");
1048                 }
1049             } else {
1050                 Die(errno, ti->data);
1051             }
1052             error = 1;
1053         }
1054     }
1055     if (ta)
1056         ZapAcl(ta);
1057     return error;
1058 }
1059
1060 static int 
1061 CopyACLCmd(struct cmd_syndesc *as, void *arock)
1062 {
1063     afs_int32 code;
1064     struct ViceIoctl blob;
1065     struct Acl *fa, *ta = 0;
1066     struct AclEntry *tp;
1067     struct cmd_item *ti;
1068     int clear;
1069     int idf = getidf(as, parm_copyacl_id);
1070     int error = 0;
1071
1072     if (as->parms[2].items) 
1073         clear=1;
1074     else 
1075         clear=0;
1076     blob.out_size = MAXSIZE;
1077     blob.in_size = idf;
1078     blob.in = blob.out = space;
1079     code = pioctl_utf8(as->parms[0].items->data, VIOCGETAL, &blob, 1);
1080     if (code) {
1081         Die(errno, as->parms[0].items->data);
1082         return 1;
1083     }
1084     fa = ParseAcl(space);
1085     if (!fa) {
1086         fprintf(stderr,
1087                  "fs: %s: invalid acl data returned from VIOCGETAL\n",
1088                  as->parms[0].items->data);
1089         return 1;
1090     }
1091     CleanAcl(fa, as->parms[0].items->data);
1092     for (ti=as->parms[1].items; ti;ti=ti->next) {
1093         blob.out_size = MAXSIZE;
1094         blob.in_size = idf;
1095         blob.in = blob.out = space;
1096         code = pioctl_utf8(ti->data, VIOCGETAL, &blob, 1);
1097         if (code) {
1098             Die(errno, ti->data);
1099             error = 1;
1100             continue;
1101         }
1102         if (ta)
1103             ZapAcl(ta);
1104         if (clear) 
1105             ta = EmptyAcl(space);
1106         else 
1107             ta = ParseAcl(space);
1108         if (!ta) {
1109             fprintf(stderr,
1110                     "fs: %s: invalid acl data returned from VIOCGETAL\n",
1111                      ti->data);
1112             error = 1;
1113             continue;
1114         }
1115         CleanAcl(ta, ti->data);
1116         if (ta->dfs != fa->dfs) {
1117             fprintf(stderr, 
1118                     "%s: incompatible file system types: acl not copied to %s; aborted\n", 
1119                     pn, ti->data);
1120             error = 1;
1121             continue;
1122         }
1123         if (ta->dfs) {
1124             if (! clear && strcmp(ta->cell, fa->cell) != 0) {
1125                 fprintf(stderr, 
1126                         "%s: default DCE cell differs for file %s: use \"-clear\" switch; acl not merged\n", 
1127                         pn, ti->data);
1128                 error = 1;
1129                 continue;
1130             }
1131             strcpy(ta->cell, fa->cell);
1132         }
1133         for (tp = fa->pluslist;tp;tp=tp->next) 
1134             ChangeList(ta, 1, tp->name, tp->rights);
1135         for (tp = fa->minuslist;tp;tp=tp->next) 
1136             ChangeList(ta, 0, tp->name, tp->rights);
1137         blob.in = AclToString(ta);
1138         blob.out_size=0;
1139         blob.in_size = 1+(long)strlen(blob.in);
1140         code = pioctl_utf8(ti->data, VIOCSETAL, &blob, 1);
1141         if (code) {
1142             if (errno == EINVAL) {
1143                 fprintf(stderr,
1144                         "%s: Invalid argument, possible reasons include:\n", pn);
1145                 fprintf(stderr,"\t-File not in AFS\n");
1146             } else {
1147                 Die(errno, ti->data);
1148             }
1149             error = 1;
1150         }
1151     } 
1152     if (ta)
1153         ZapAcl(ta);
1154     ZapAcl(fa);
1155     return error;
1156 }
1157
1158 /* pioctl_utf8() call to get the cellname of a pathname */
1159 static afs_int32
1160 GetCell(char *fname, char *cellname)
1161 {
1162     afs_int32 code;
1163     struct ViceIoctl blob;
1164
1165     blob.in_size = 0;
1166     blob.out_size = CELL_MAXNAMELEN;
1167     blob.out = cellname;
1168
1169     code = pioctl_utf8(fname, VIOC_FILE_CELL_NAME, &blob, 1);
1170     return code;
1171 }
1172
1173 /* Check if a username is valid: If it contains only digits (or a
1174  * negative sign), then it might be bad.  We then query the ptserver
1175  * to see.
1176  */
1177 static int
1178 BadName(char *aname, char *fname)
1179 {
1180     afs_int32 tc, code, id;
1181     char *nm;
1182     char cell[CELL_MAXNAMELEN];
1183     char confDir[257];
1184
1185     for ( nm = aname; tc = *nm; nm++) {
1186         /* all must be '-' or digit to be bad */
1187         if (tc != '-' && (tc < '0' || tc > '9'))
1188             return 0;
1189     }
1190
1191     /* Go to the PRDB and see if this all number username is valid */
1192     code = GetCell(fname, cell);
1193     if (code)
1194         return 0;
1195
1196     cm_GetConfigDir(confDir, sizeof(confDir));
1197
1198     pr_Initialize(1, confDir, cell);
1199     code = pr_SNameToId(aname, &id);
1200     pr_End();
1201
1202     /* 1=>Not-valid; 0=>Valid */
1203     return ((!code && (id == ANONYMOUSID)) ? 1 : 0);
1204 }
1205
1206
1207 /* clean up an access control list of its bad entries; return 1 if we made
1208    any changes to the list, and 0 otherwise */
1209 static int 
1210 CleanAcl(struct Acl *aa, char *fname)
1211 {
1212     struct AclEntry *te, **le, *ne;
1213     int changes;
1214
1215     /* Don't correct DFS ACL's for now */
1216     if (aa->dfs)
1217         return 0;
1218
1219     /* prune out bad entries */
1220     changes = 0;            /* count deleted entries */
1221     le = &aa->pluslist;
1222     for(te = aa->pluslist; te; te=ne) {
1223         ne = te->next;
1224         if (BadName(te->name, fname)) {
1225             /* zap this dude */
1226             *le = te->next;
1227             aa->nplus--;
1228             free(te);
1229             changes++;
1230         } else {
1231             le = &te->next;
1232         }
1233     }
1234     le = &aa->minuslist;
1235     for(te = aa->minuslist; te; te=ne) {
1236         ne = te->next;
1237         if (BadName(te->name, fname)) {
1238             /* zap this dude */
1239             *le = te->next;
1240             aa->nminus--;
1241             free(te);
1242             changes++;
1243         } else {
1244             le = &te->next;
1245         }
1246     }
1247     return changes;
1248 }
1249
1250
1251 /* clean up an acl to not have bogus entries */
1252 static int 
1253 CleanACLCmd(struct cmd_syndesc *as, void *arock)
1254 {
1255     afs_int32 code;
1256     struct Acl *ta = 0;
1257     struct ViceIoctl blob;
1258     int changes;
1259     struct cmd_item *ti;
1260     struct AclEntry *te;
1261     int error = 0;
1262
1263     SetDotDefault(&as->parms[0].items);
1264     for(ti=as->parms[0].items; ti; ti=ti->next) {
1265         blob.out_size = MAXSIZE;
1266         blob.in_size = 0;
1267         blob.out = space;
1268         code = pioctl_utf8(ti->data, VIOCGETAL, &blob, 1);
1269         if (code) {
1270             Die(errno, ti->data);
1271             error = 1;
1272             continue;
1273         }
1274         if (ta)
1275             ZapAcl(ta);
1276         ta = ParseAcl(space);
1277         if (!ta) {
1278             fprintf(stderr,
1279                     "fs: %s: invalid acl data returned from VIOCGETAL\n",
1280                      ti->data);
1281             error = 1;
1282             continue;
1283         }
1284         if (ta->dfs) {
1285             fprintf(stderr,
1286                     "%s: cleanacl is not supported for DFS access lists.\n",
1287                     pn);
1288             error = 1;
1289             continue;
1290         }
1291
1292         changes = CleanAcl(ta, ti->data);
1293
1294         if (changes) {
1295             /* now set the acl */
1296             blob.in=AclToString(ta);
1297             blob.in_size = (long)strlen(blob.in)+1;
1298             blob.out_size = 0;
1299             code = pioctl_utf8(ti->data, VIOCSETAL, &blob, 1);
1300             if (code) {
1301                 if (errno == EINVAL) {
1302                     fprintf(stderr,
1303                             "%s: Invalid argument, possible reasons include\n", 
1304                              pn);
1305                     fprintf(stderr,"%s: File not in vice or\n", pn);
1306                     fprintf(stderr,
1307                             "%s: Too many users on access control list or\n", 
1308                             pn);
1309                 } else {
1310                     Die(errno, ti->data);
1311                 }
1312                 error = 1;
1313                 continue;
1314             }
1315
1316             /* now list the updated acl */
1317             printf("Access list for %s is now\n", ti->data);
1318             if (ta->nplus > 0) {
1319                 if (!ta->dfs) 
1320                     printf("Normal rights:\n");
1321                 for(te = ta->pluslist;te;te=te->next) {
1322                     printf("  %s ", te->name);
1323                     PRights(te->rights, ta->dfs);
1324                     printf("\n");
1325                 }
1326             }
1327             if (ta->nminus > 0) {
1328                 printf("Negative rights:\n");
1329                 for(te = ta->minuslist;te;te=te->next) {
1330                     printf("  %s ", te->name);
1331                     PRights(te->rights, ta->dfs);
1332                     printf("\n");
1333                 }
1334             }
1335             if (ti->next) 
1336                 printf("\n");
1337         } else
1338             printf("Access list for %s is fine.\n", ti->data);
1339     }
1340     if (ta)
1341         ZapAcl(ta);
1342     return error;
1343 }
1344
1345 static int 
1346 ListACLCmd(struct cmd_syndesc *as, void *arock) 
1347 {
1348     afs_int32 code;
1349     struct Acl *ta = 0;
1350     struct ViceIoctl blob;
1351     struct AclEntry *te;
1352     struct cmd_item *ti;
1353     int idf = getidf(as, parm_listacl_id);
1354     int error = 0;
1355
1356     SetDotDefault(&as->parms[0].items);
1357     for(ti=as->parms[0].items; ti; ti=ti->next) {
1358         char separator;
1359         blob.out_size = MAXSIZE;
1360         blob.in_size = idf;
1361         blob.in = blob.out = space;
1362         code = pioctl_utf8(ti->data, VIOCGETAL, &blob, 1);
1363         if (code) {
1364             Die(errno, ti->data);
1365             error = 1;
1366             continue;
1367         }
1368         ta = ParseAcl(space);
1369         if (!ta) {
1370             fprintf(stderr,
1371                     "fs: %s: invalid acl data returned from VIOCGETAL\n",
1372                      ti->data);
1373             error = 1;
1374             continue;
1375         }
1376         switch (ta->dfs) {
1377           case 0:
1378             printf("Access list for %s is\n", ti->data);
1379             break;
1380           case 1:
1381             printf("DFS access list for %s is\n", ti->data);
1382             break;
1383           case 2:
1384             printf("DFS initial directory access list of %s is\n", ti->data);
1385             break;
1386           case 3:
1387             printf("DFS initial file access list of %s is\n", ti->data);
1388             break;
1389         }
1390         if (ta->dfs) {
1391             printf("  Default cell = %s\n", ta->cell);
1392         }
1393         separator = ta->dfs? DFS_SEPARATOR : ' ';
1394         if (ta->nplus > 0) {
1395             if (!ta->dfs) 
1396                 printf("Normal rights:\n");
1397             for(te = ta->pluslist;te;te=te->next) {
1398                 printf("  %s%c", te->name, separator);
1399                 PRights(te->rights, ta->dfs);
1400                 printf("\n");
1401             }
1402         }
1403         if (ta->nminus > 0) {
1404             printf("Negative rights:\n");
1405             for(te = ta->minuslist;te;te=te->next) {
1406                 printf("  %s ", te->name);
1407                 PRights(te->rights, ta->dfs);
1408                 printf("\n");
1409             }
1410         }
1411         if (ti->next) 
1412             printf("\n");
1413         ZapAcl(ta);
1414     }
1415     return error;
1416 }
1417
1418 static int
1419 FlushAllCmd(struct cmd_syndesc *as, void *arock)
1420 {
1421     afs_int32 code;
1422     struct ViceIoctl blob;
1423
1424     blob.in_size = blob.out_size = 0;
1425     code = pioctl_utf8(NULL, VIOC_FLUSHALL, &blob, 0);
1426     if (code) {
1427         fprintf(stderr, "Error flushing all ");
1428         return 1;
1429     }
1430     return 0;
1431 }
1432
1433 static int
1434 FlushVolumeCmd(struct cmd_syndesc *as, void *arock)
1435 {
1436     afs_int32 code;
1437     struct ViceIoctl blob;
1438     struct cmd_item *ti;
1439     int error = 0;
1440
1441     SetDotDefault(&as->parms[0].items);
1442     for(ti=as->parms[0].items; ti; ti=ti->next) {
1443         blob.in_size = blob.out_size = 0;
1444         code = pioctl_utf8(ti->data, VIOC_FLUSHVOLUME, &blob, 0);
1445         if (code) {
1446             fprintf(stderr, "Error flushing volume ");
1447             perror(ti->data);
1448             error = 1;
1449             continue;
1450         }
1451     }
1452     return error;
1453 }
1454
1455 static int 
1456 FlushCmd(struct cmd_syndesc *as, void *arock) 
1457 {
1458     afs_int32 code;
1459     struct ViceIoctl blob;
1460     struct cmd_item *ti;
1461     int error = 0;
1462     int literal = 0;
1463     cm_ioctlQueryOptions_t options;
1464
1465     if (as->parms[1].items)
1466         literal = 1;
1467     
1468     for(ti=as->parms[0].items; ti; ti=ti->next) {
1469         /* once per file */
1470         memset(&options, 0, sizeof(options));
1471         options.size = sizeof(options);
1472         options.field_flags |= CM_IOCTL_QOPTS_FIELD_LITERAL;
1473         options.literal = literal;
1474         blob.in_size = options.size;    /* no variable length data */
1475         blob.in = &options;
1476
1477         blob.out_size = 0;
1478         code = pioctl_utf8(ti->data, VIOCFLUSH, &blob, 0);
1479         if (code) {
1480             if (errno == EMFILE) {
1481                 fprintf(stderr, "%s: Can't flush active file %s\n", pn, 
1482                         ti->data);
1483             } else {
1484                 fprintf(stderr, "%s: Error flushing file ", pn);
1485                 perror(ti->data);
1486             }
1487             error = 1;
1488             continue;
1489         }
1490     }
1491     return error;
1492 }
1493
1494 /* all this command does is repackage its args and call SetVolCmd */
1495 static int
1496 SetQuotaCmd(struct cmd_syndesc *as, void *arock) {
1497     struct cmd_syndesc ts;
1498
1499     /* copy useful stuff from our command slot; we may later have to reorder */
1500     memcpy(&ts, as, sizeof(ts));        /* copy whole thing */
1501     return SetVolCmd(&ts, arock);
1502 }
1503
1504 static int
1505 SetVolCmd(struct cmd_syndesc *as, void *arock) {
1506     afs_int32 code;
1507     struct ViceIoctl blob;
1508     struct cmd_item *ti;
1509     struct VolumeStatus *status;
1510     char *motd, *offmsg, *input;
1511     int error = 0;
1512
1513     SetDotDefault(&as->parms[0].items);
1514     for(ti=as->parms[0].items; ti; ti=ti->next) {
1515         /* once per file */
1516         blob.out_size = MAXSIZE;
1517         blob.in_size = sizeof(*status) + 3;     /* for the three terminating nulls */
1518         blob.out = space;
1519         blob.in = space;
1520         status = (VolumeStatus *)space;
1521         status->MinQuota = status->MaxQuota = -1;
1522         motd = offmsg = NULL;
1523         if (as->parms[1].items) {
1524             code = util_GetInt32(as->parms[1].items->data, &status->MaxQuota);
1525             if (code) {
1526                 fprintf(stderr,"%s: bad integer specified for quota.\n", pn);
1527                 error = 1;
1528                 continue;
1529             }
1530         }
1531         if (as->parms[2].items) 
1532             motd = as->parms[2].items->data;
1533         if (as->parms[3].items) 
1534             offmsg = as->parms[3].items->data;
1535         input = (char *)status + sizeof(*status);
1536         *(input++) = '\0';      /* never set name: this call doesn't change vldb */
1537         if(offmsg) {
1538             if (strlen(offmsg) >= VMSGSIZE) {
1539                 fprintf(stderr,"%s: message must be shorter than %d characters\n",
1540                          pn, VMSGSIZE);
1541                 error = 1;
1542                 continue;
1543             }
1544             strcpy(input,offmsg);
1545             blob.in_size += (long)strlen(offmsg);
1546             input += strlen(offmsg) + 1;
1547         } else 
1548             *(input++) = '\0';
1549         if(motd) {
1550             if (strlen(motd) >= VMSGSIZE) {
1551                 fprintf(stderr,"%s: message must be shorter than %d characters\n",
1552                          pn, VMSGSIZE);
1553                 return code;
1554             }
1555             strcpy(input,motd);
1556             blob.in_size += (long)strlen(motd);
1557             input += strlen(motd) + 1;
1558         } else 
1559             *(input++) = '\0';
1560         code = pioctl_utf8(ti->data,VIOCSETVOLSTAT, &blob, 1);
1561         if (code) {
1562             Die(errno, ti->data);
1563             error = 1;
1564         }
1565     }
1566     return error;
1567 }
1568
1569 /* values match cache manager File Types */
1570 static char *
1571 filetypestr(afs_uint32 type)
1572 {
1573     char * s = "Object";
1574
1575     switch (type) {
1576     case 1:     /* file */
1577         s = "File";
1578         break;
1579     case 2:
1580         s = "Directory";
1581         break;
1582     case 3:
1583         s = "Symlink";
1584         break;
1585     case 4:
1586         s = "Mountpoint";
1587         break;
1588     case 5:
1589         s = "DfsLink";
1590         break;
1591     }
1592     return s;
1593 }
1594
1595 static int 
1596 ExamineCmd(struct cmd_syndesc *as, void *arock)
1597 {
1598     afs_int32 code;
1599     struct ViceIoctl blob;
1600     struct cmd_item *ti;
1601     struct VolumeStatus *status;
1602     char *name, *offmsg, *motd;
1603     int error = 0;
1604     int literal = 0;
1605     cm_ioctlQueryOptions_t options;
1606
1607     if (as->parms[1].items)
1608         literal = 1;
1609
1610     SetDotDefault(&as->parms[0].items);
1611     for(ti=as->parms[0].items; ti; ti=ti->next) {
1612         cm_fid_t fid;
1613         afs_uint32 filetype;
1614         afs_uint32 owner[2];
1615         char cell[CELL_MAXNAMELEN];
1616
1617         /* once per file */
1618         memset(&fid, 0, sizeof(fid));
1619         memset(&options, 0, sizeof(options));
1620         filetype = 0;
1621         options.size = sizeof(options);
1622         options.field_flags |= CM_IOCTL_QOPTS_FIELD_LITERAL;
1623         options.literal = literal;
1624         blob.in_size = options.size;    /* no variable length data */
1625         blob.in = &options;
1626
1627         blob.out_size = sizeof(cm_fid_t);
1628         blob.out = (char *) &fid;
1629         if (0 == pioctl_utf8(ti->data, VIOCGETFID, &blob, 1)) {
1630             options.field_flags |= CM_IOCTL_QOPTS_FIELD_FID;
1631             options.fid = fid;
1632         } else {
1633             Die(errno, ti->data);
1634             error = 1;
1635             continue;
1636         }
1637
1638         blob.out_size = sizeof(filetype);
1639         blob.out = &filetype;
1640
1641         code = pioctl_utf8(ti->data, VIOC_GETFILETYPE, &blob, 1);
1642
1643         blob.out_size = CELL_MAXNAMELEN;
1644         blob.out = cell;
1645
1646         code = pioctl_utf8(ti->data, VIOC_FILE_CELL_NAME, &blob, 1);
1647         printf("%s %s (%u.%u.%u) contained in cell %s\n",
1648                 filetypestr(filetype),
1649                 ti->data, fid.volume, fid.vnode, fid.unique,
1650                 code ? "unknown-cell" : cell);
1651
1652         blob.out_size = 2 * sizeof(afs_uint32);
1653         blob.out = (char *) &owner;
1654         if (0 == pioctl_utf8(ti->data, VIOCGETOWNER, &blob, 1)) {
1655             char oname[PR_MAXNAMELEN] = "(unknown)";
1656             char confDir[257];
1657
1658             /* Go to the PRDB and see if this all number username is valid */
1659             cm_GetConfigDir(confDir, sizeof(confDir));
1660
1661             pr_Initialize(1, confDir, cell);
1662             pr_SIdToName(owner[0], oname);
1663             printf("Owner %s (%u) Group %u\n", oname, owner[0], owner[1]);
1664         }
1665
1666         blob.out = space;
1667         blob.out_size = MAXSIZE;
1668         code = pioctl_utf8(ti->data, VIOCGETVOLSTAT, &blob, 1);
1669         if (code == 0) {
1670             status = (VolumeStatus *)space;
1671             name = (char *)status + sizeof(*status);
1672             offmsg = name + strlen(name) + 1;
1673             motd = offmsg + strlen(offmsg) + 1;
1674
1675             PrintStatus(status, name, motd, offmsg);
1676         } else {
1677             Die(errno, ti->data);
1678         }
1679
1680         errno = 0;
1681         code = pioctl_utf8(ti->data, VIOC_PATH_AVAILABILITY, &blob, 1);
1682         switch (errno) {
1683         case 0:
1684             printf("Volume is online\n");
1685             break;
1686         case ENXIO:
1687             printf("Volume is offline\n");
1688             break;
1689         case ENOSYS:
1690             printf("All Volume servers are down\n");
1691             break;
1692         case EBUSY:
1693             printf("All volume servers are busy\n");
1694             break;
1695         default:
1696             printf("Unknown volume state\n");
1697             Die(errno, ti->data);
1698         }
1699         printf("\n");
1700     }
1701     return error;
1702 }
1703
1704 static int
1705 ListQuotaCmd(struct cmd_syndesc *as, void *arock) 
1706 {
1707     afs_int32 code;
1708     struct ViceIoctl blob;
1709     struct cmd_item *ti;
1710     struct VolumeStatus *status;
1711     char *name;
1712
1713     int error = 0;
1714     
1715     printf("%-25s%-10s%-10s%-7s%-13s\n", 
1716            "Volume Name", "     Quota", "      Used", "  %Used", "    Partition");
1717     SetDotDefault(&as->parms[0].items);
1718     for(ti=as->parms[0].items; ti; ti=ti->next) {
1719         /* once per file */
1720         blob.out_size = MAXSIZE;
1721         blob.in_size = 0;
1722         blob.out = space;
1723         code = pioctl_utf8(ti->data, VIOCGETVOLSTAT, &blob, 1);
1724         if (code) {
1725             Die(errno, ti->data);
1726             error = 1;
1727             continue;
1728         }
1729         status = (VolumeStatus *)space;
1730         name = (char *)status + sizeof(*status);
1731         QuickPrintStatus(status, name);
1732     }
1733     return error;
1734 }
1735
1736 static int
1737 WhereIsCmd(struct cmd_syndesc *as, void *arock)
1738 {
1739     afs_int32 code;
1740     struct ViceIoctl blob;
1741     struct cmd_item *ti;
1742     int j;
1743     afs_int32 *hosts;
1744     char *tp;
1745     int error = 0;
1746     int literal = 0;
1747     cm_ioctlQueryOptions_t options;
1748
1749     if (as->parms[1].items)
1750         literal = 1;
1751     
1752     SetDotDefault(&as->parms[0].items);
1753     for(ti=as->parms[0].items; ti; ti=ti->next) {
1754         cm_fid_t fid;
1755         afs_uint32 filetype;
1756
1757         /* once per file */
1758         memset(&fid, 0, sizeof(fid));
1759         memset(&options, 0, sizeof(options));
1760         filetype = 0;
1761         options.size = sizeof(options);
1762         options.field_flags |= CM_IOCTL_QOPTS_FIELD_LITERAL;
1763         options.literal = literal;
1764         blob.in_size = options.size;    /* no variable length data */
1765         blob.in = &options;
1766         
1767         blob.out_size = sizeof(cm_fid_t);
1768         blob.out = (char *) &fid;
1769         if (0 == pioctl_utf8(ti->data, VIOCGETFID, &blob, 1)) {
1770             options.field_flags |= CM_IOCTL_QOPTS_FIELD_FID;
1771             options.fid = fid;
1772         } else {
1773             Die(errno, ti->data);
1774             error = 1;
1775             continue;
1776         }
1777
1778         blob.out_size = sizeof(filetype);
1779         blob.out = &filetype;
1780
1781         code = pioctl_utf8(ti->data, VIOC_GETFILETYPE, &blob, 1);
1782
1783         blob.out_size = MAXSIZE;
1784         blob.out = space;
1785         memset(space, 0, sizeof(space));
1786         code = pioctl_utf8(ti->data, VIOCWHEREIS, &blob, 1);
1787         if (code) {
1788             Die(errno, ti->data);
1789             error = 1;
1790             continue;
1791         }
1792         hosts = (afs_int32 *) space;
1793         printf("%s %s is on host%s ", 
1794                 filetypestr(filetype),
1795                 ti->data,
1796                 (hosts[0] && !hosts[1]) ? "": "s");
1797         for(j=0; j<MAXHOSTS; j++) {
1798             if (hosts[j] == 0) 
1799                 break;
1800             tp = hostutil_GetNameByINet(hosts[j]);
1801             printf("%s ", tp);
1802         }
1803         printf("\n");
1804     }
1805     return error;
1806 }
1807
1808
1809 static int
1810 DiskFreeCmd(struct cmd_syndesc *as, void *arock)
1811 {
1812     afs_int32 code;
1813     struct ViceIoctl blob;
1814     struct cmd_item *ti;
1815     char *name;
1816     struct VolumeStatus *status;
1817     int error = 0;
1818     
1819     printf("%-25s%-10s%-10s%-10s%-6s\n", "Volume Name", "    kbytes",
1820            "      used", "     avail", " %used");
1821     SetDotDefault(&as->parms[0].items);
1822     for(ti=as->parms[0].items; ti; ti=ti->next) {
1823         /* once per file */
1824         blob.out_size = MAXSIZE;
1825         blob.in_size = 0;
1826         blob.out = space;
1827         code = pioctl_utf8(ti->data, VIOCGETVOLSTAT, &blob, 1);
1828         if (code) {
1829             Die(errno, ti->data);
1830             error = 1;
1831             continue;
1832         }
1833         status = (VolumeStatus *)space;
1834         name = (char *)status + sizeof(*status);
1835         QuickPrintSpace(status, name);
1836     }
1837     return error;
1838 }
1839
1840 static int
1841 QuotaCmd(struct cmd_syndesc *as, void *arock)
1842 {
1843     afs_int32 code;
1844     struct ViceIoctl blob;
1845     struct cmd_item *ti;
1846     double quotaPct;
1847     struct VolumeStatus *status;
1848     int error = 0;
1849     
1850     SetDotDefault(&as->parms[0].items);
1851     for(ti=as->parms[0].items; ti; ti=ti->next) {
1852         /* once per file */
1853         blob.out_size = MAXSIZE;
1854         blob.in_size = 0;
1855         blob.out = space;
1856         code = pioctl_utf8(ti->data, VIOCGETVOLSTAT, &blob, 1);
1857         if (code) {
1858             Die(errno, ti->data);
1859             error = 1;
1860             continue;
1861         }
1862         status = (VolumeStatus *)space;
1863         if (status->MaxQuota) 
1864             quotaPct = ((((double)status->BlocksInUse)/status->MaxQuota) * 100.0);
1865         else 
1866             quotaPct = 0.0;
1867         printf("%2.0f%% of quota used.\n", quotaPct);
1868     }
1869     return error;
1870 }
1871
1872 static int
1873 ListMountCmd(struct cmd_syndesc *as, void *arock)
1874 {
1875     afs_int32 code;
1876     struct ViceIoctl blob;
1877     struct cmd_item *ti;
1878     char orig_name[1024];               /*Original name, may be modified*/
1879     char true_name[1024];               /*``True'' dirname (e.g., symlink target)*/
1880     char parent_dir[1024];              /*Parent directory of true name*/
1881     char *last_component;       /*Last component of true name*/
1882 #ifndef WIN32
1883     struct stat statbuff;               /*Buffer for status info*/
1884 #endif /* not WIN32 */
1885 #ifndef WIN32
1886     int link_chars_read;                /*Num chars read in readlink()*/
1887 #endif /* not WIN32 */
1888     int thru_symlink;                   /*Did we get to a mount point via a symlink?*/
1889     
1890     int error = 0;
1891     for(ti=as->parms[0].items; ti; ti=ti->next) {
1892         /* once per file */
1893         thru_symlink = 0;
1894 #ifdef WIN32
1895         strcpy(orig_name, ti->data);
1896 #else /* not WIN32 */
1897         sprintf(orig_name, "%s%s",
1898                 (ti->data[0] == '/') ? "" : "./",
1899                 ti->data);
1900 #endif /* not WIN32 */
1901
1902 #ifndef WIN32
1903         if (lstat(orig_name, &statbuff) < 0) {
1904             /* if lstat fails, we should still try the pioctl, since it
1905              * may work (for example, lstat will fail, but pioctl will
1906              * work if the volume of offline (returning ENODEV). */
1907             statbuff.st_mode = S_IFDIR; /* lie like pros */
1908         }
1909
1910         /*
1911          * The lstat succeeded.  If the given file is a symlink, substitute
1912          * the file name with the link name.
1913          */
1914         if ((statbuff.st_mode & S_IFMT) == S_IFLNK) {
1915             thru_symlink = 1;
1916             /*
1917              * Read name of resolved file.
1918              */
1919             link_chars_read = readlink(orig_name, true_name, 1024);
1920             if (link_chars_read <= 0) {
1921                 fprintf(stderr,
1922                         "%s: Can't read target name for '%s' symbolic link!\n",
1923                        pn, orig_name);
1924                 error = 1;
1925                 continue;
1926             }
1927
1928             /*
1929              * Add a trailing null to what was read, bump the length.
1930              */
1931             true_name[link_chars_read++] = 0;
1932
1933             /*
1934              * If the symlink is an absolute pathname, we're fine.  Otherwise, we
1935              * have to create a full pathname using the original name and the
1936              * relative symlink name.  Find the rightmost slash in the original
1937              * name (we know there is one) and splice in the symlink value.
1938              */
1939             if (true_name[0] != '\\') {
1940                 last_component = (char *) strrchr(orig_name, '\\');
1941                 strcpy(++last_component, true_name);
1942                 strcpy(true_name, orig_name);
1943             }
1944         } else
1945             strcpy(true_name, orig_name);
1946 #else   /* WIN32 */
1947         strcpy(true_name, orig_name);
1948 #endif /* WIN32 */
1949
1950         /*
1951          * Find rightmost slash, if any.
1952          */
1953 #ifdef WIN32
1954         last_component = (char *) strrchr(true_name, '\\');
1955         if (!last_component)
1956 #endif /* WIN32 */
1957             last_component = (char *) strrchr(true_name, '/');
1958         if (last_component) {
1959             /*
1960              * Found it.  Designate everything before it as the parent directory,
1961              * everything after it as the final component.
1962              */
1963             strncpy(parent_dir, true_name, last_component - true_name + 1);
1964             parent_dir[last_component - true_name + 1] = 0;
1965             last_component++;   /*Skip the slash*/
1966 #ifdef WIN32
1967             if (!InAFS(parent_dir)) {
1968                 const char * nbname = NetbiosName();
1969                 int len = (int)strlen(nbname);
1970
1971                 if (parent_dir[0] == '\\' && parent_dir[1] == '\\' &&
1972                     parent_dir[len+2] == '\\' &&
1973                     parent_dir[len+3] == '\0' &&
1974                     !strnicmp(nbname,&parent_dir[2],len))
1975                 {
1976                     sprintf(parent_dir,"\\\\%s\\all\\", nbname);
1977                 }
1978             }
1979 #endif
1980         } else {
1981             /*
1982              * No slash appears in the given file name.  Set parent_dir to the current
1983              * directory, and the last component as the given name.
1984              */
1985             fs_ExtractDriveLetter(true_name, parent_dir);
1986             strcat(parent_dir, ".");
1987             last_component = true_name;
1988             fs_StripDriveLetter(true_name, true_name, sizeof(true_name));
1989         }
1990
1991         if (strcmp(last_component, ".") == 0 || strcmp(last_component, "..") == 0) {
1992             fprintf(stderr,"%s: you may not use '.' or '..' as the last component\n",pn);
1993             fprintf(stderr,"%s: of a name in the 'fs lsmount' command.\n",pn);
1994             error = 1;
1995             continue;
1996         }
1997
1998         blob.in = last_component;
1999         blob.in_size = (long)strlen(last_component)+1;
2000         blob.out_size = MAXSIZE;
2001         blob.out = space;
2002         memset(space, 0, MAXSIZE);
2003
2004         code = pioctl_utf8(parent_dir, VIOC_AFS_STAT_MT_PT, &blob, 1);
2005
2006         if (code == 0) {
2007             printf("'%s' is a %smount point for volume '%s'\n",
2008                    ti->data,
2009                    (thru_symlink ? "symbolic link, leading to a " : ""),
2010                    space);
2011
2012         } else {
2013             if (errno == EINVAL) {
2014                 fprintf(stderr,"'%s' is not a mount point.\n", ti->data);
2015             } else {
2016                 Die(errno, (ti->data ? ti->data : parent_dir));
2017             }
2018             error = 1;
2019         }
2020     }
2021     return error;
2022 }
2023
2024 static int
2025 MakeMountCmd(struct cmd_syndesc *as, void *arock)
2026 {
2027     afs_int32 code;
2028     char *cellName, *volName, *tmpName;
2029 #ifdef WIN32
2030     char localCellName[128];
2031 #endif
2032     char path[1024] = "";
2033     struct afsconf_cell info;
2034     struct vldbentry vldbEntry;
2035     struct ViceIoctl blob;
2036     char * parent;
2037
2038     memset(&info, 0, sizeof(info));
2039
2040     if (as->parms[2].items)     /* cell name specified */
2041         cellName = as->parms[2].items->data;
2042     else
2043         cellName = NULL;
2044     volName = as->parms[1].items->data;
2045
2046     if (strlen(volName) >= 64) {
2047         fprintf(stderr,"%s: volume name too long (length must be < 64 characters)\n", pn);
2048         return 1;
2049     }
2050
2051     /* Check for a cellname in the volume specification, and complain
2052      * if it doesn't match what was specified with -cell */
2053     if (tmpName = strchr(volName, ':')) {
2054         *tmpName = '\0';
2055         if (cellName) {
2056             if (strcasecmp(cellName,volName)) {
2057                 fprintf(stderr,"fs: cellnames do not match.\n");
2058                 return 1;
2059             }
2060         }
2061         cellName = volName;
2062         volName = ++tmpName;
2063     }
2064
2065     parent = Parent(as->parms[0].items->data);
2066     if (!InAFS(parent)) {
2067 #ifdef WIN32
2068         const char * nbname = NetbiosName();
2069         int len = (int)strlen(nbname);
2070
2071         if (parent[0] == '\\' && parent[1] == '\\' &&
2072             parent[len+2] == '\\' &&
2073             parent[len+3] == '\0' &&
2074             !strnicmp(nbname,&parent[2],len))
2075         {
2076             sprintf(path,"%sall\\%s", parent, &as->parms[0].items->data[strlen(parent)]);
2077             parent = Parent(path);
2078             if (!InAFS(parent)) {
2079                 fprintf(stderr,"%s: mount points must be created within the AFS file system\n", pn);
2080                 return 1;
2081             }
2082         } else 
2083 #endif
2084         {
2085             fprintf(stderr,"%s: mount points must be created within the AFS file system\n", pn);
2086             return 1;
2087         }
2088     }
2089
2090     if ( strlen(path) == 0 )
2091         strcpy(path, as->parms[0].items->data);
2092
2093     if ( IsFreelanceRoot(parent) ) {
2094         if ( !IsAdmin() ) {
2095             fprintf(stderr,"%s: Only AFS Client Administrators may alter the root.afs volume\n", pn);
2096             return 1;
2097         }
2098
2099         if (!cellName) {
2100             blob.in_size = 0;
2101             blob.out_size = sizeof(localCellName);
2102             blob.out = localCellName;
2103             code = pioctl_utf8(parent, VIOC_GET_WS_CELL, &blob, 1);
2104             if (!code)
2105                 cellName = localCellName;
2106         }
2107     } else {
2108         if (!cellName) {
2109             code = GetCell(parent,space);
2110             if (code)
2111                 return 1;
2112         }
2113     }
2114
2115     code = GetCellName(cellName?cellName:space, &info);
2116     if (code) {
2117         return 1;
2118     }
2119     if (!(as->parms[4].items)) {
2120       /* not fast, check which cell the mountpoint is being created in */
2121       code = 0;
2122         /* not fast, check name with VLDB */
2123       if (!code)
2124         code = VLDBInit(1, &info);
2125       if (code == 0) {
2126           /* make the check.  Don't complain if there are problems with init */
2127           code = ubik_VL_GetEntryByNameO(uclient, 0, volName, &vldbEntry);
2128           if (code == VL_NOENT) {
2129               fprintf(stderr,"%s: warning, volume %s does not exist in cell %s.\n",
2130                       pn, volName, cellName ? cellName : space);
2131           }
2132       }
2133     }
2134
2135     if (as->parms[3].items)     /* if -rw specified */
2136         strcpy(space, "%");
2137     else
2138         strcpy(space, "#");
2139     if (cellName) {
2140         /* cellular mount point, prepend cell prefix */
2141         strcat(space, info.name);
2142         strcat(space, ":");
2143     }
2144     strcat(space, volName);     /* append volume name */
2145     strcat(space, ".");         /* stupid convention; these end with a period */
2146 #ifdef WIN32
2147     /* create symlink with a special pioctl for Windows NT, since it doesn't
2148      * have a symlink system call.
2149      */
2150     blob.out_size = 0;
2151     blob.in_size = 1 + (long)strlen(space);
2152     blob.in = space;
2153     blob.out = NULL;
2154     code = pioctl_utf8(path, VIOC_AFS_CREATE_MT_PT, &blob, 0);
2155 #else /* not WIN32 */
2156     code = symlink(space, path);
2157 #endif /* not WIN32 */
2158
2159     if (info.linkedCell)
2160         free(info.linkedCell);
2161
2162     if (code) {
2163         Die(errno, path);
2164         return 1;
2165     }
2166     return 0;
2167 }
2168
2169 /*
2170  * Delete AFS mount points.  Variables are used as follows:
2171  *       tbuffer: Set to point to the null-terminated directory name of the mount point
2172  *          (or ``.'' if none is provided)
2173  *      tp: Set to point to the actual name of the mount point to nuke.
2174  */
2175 static int
2176 RemoveMountCmd(struct cmd_syndesc *as, void *arock) {
2177     afs_int32 code=0;
2178     struct ViceIoctl blob;
2179     struct cmd_item *ti;
2180     char tbuffer[1024];
2181     char lsbuffer[1024];
2182     char *tp;
2183     int error = 0;
2184     
2185     for(ti=as->parms[0].items; ti; ti=ti->next) {
2186         /* once per file */
2187         tp = (char *) strrchr(ti->data, '\\');
2188         if (!tp)
2189             tp = (char *) strrchr(ti->data, '/');
2190         if (tp) {
2191             strncpy(tbuffer, ti->data, code=(afs_int32)(tp-ti->data+1));  /* the dir name */
2192             tbuffer[code] = 0;
2193             tp++;   /* skip the slash */
2194
2195 #ifdef WIN32
2196             if (!InAFS(tbuffer)) {
2197                 const char * nbname = NetbiosName();
2198                 int len = (int)strlen(nbname);
2199
2200                 if (tbuffer[0] == '\\' && tbuffer[1] == '\\' &&
2201                     tbuffer[len+2] == '\\' &&
2202                     tbuffer[len+3] == '\0' &&
2203                     !strnicmp(nbname,&tbuffer[2],len))
2204                 {
2205                     sprintf(tbuffer,"\\\\%s\\all\\", nbname);
2206                 }
2207             }
2208 #endif
2209         } else {
2210             fs_ExtractDriveLetter(ti->data, tbuffer);
2211             strcat(tbuffer, ".");
2212             tp = ti->data;
2213             fs_StripDriveLetter(tp, tp, 0);
2214         }
2215         blob.in = tp;
2216         blob.in_size = (long)strlen(tp)+1;
2217         blob.out = lsbuffer;
2218         blob.out_size = sizeof(lsbuffer);
2219         code = pioctl_utf8(tbuffer, VIOC_AFS_STAT_MT_PT, &blob, 0);
2220         if (code) {
2221             if (errno == EINVAL) {
2222                 fprintf(stderr,"%s: '%s' is not a mount point.\n", pn, ti->data);
2223             } else {
2224                 Die(errno, ti->data);
2225             }
2226             error = 1;
2227             continue;   /* don't bother trying */
2228         }
2229
2230         if ( IsFreelanceRoot(tbuffer) && !IsAdmin() ) {
2231             fprintf(stderr,"%s: Only AFS Client Administrators may alter the root.afs volume\n", pn);
2232             error = 1;
2233             continue;   /* skip */
2234         }
2235
2236         blob.out_size = 0;
2237         blob.in = tp;
2238         blob.in_size = (long)strlen(tp)+1;
2239         code = pioctl_utf8(tbuffer, VIOC_AFS_DELETE_MT_PT, &blob, 0);
2240         if (code) {
2241             Die(errno, ti->data);
2242             error = 1;
2243         }
2244     }
2245     return error;
2246 }
2247
2248 /*
2249 */
2250
2251 static int
2252 CheckServersCmd(struct cmd_syndesc *as, void *arock)
2253 {
2254     afs_int32 code;
2255     struct ViceIoctl blob;
2256     afs_int32 j;
2257     afs_int32 temp;
2258     char *tp;
2259     struct afsconf_cell info;
2260     struct chservinfo checkserv;
2261
2262     memset(&info, 0, sizeof(info));
2263     memset(&checkserv, 0, sizeof(struct chservinfo));
2264     blob.in_size=sizeof(struct chservinfo);
2265     blob.in=(caddr_t)&checkserv;
2266
2267     blob.out_size = MAXSIZE;
2268     blob.out = space;
2269     memset(space, 0, sizeof(afs_int32));        /* so we assure zero when nothing is copied back */
2270
2271     /* prepare flags for checkservers command */
2272     temp = 2;   /* default to checking local cell only */
2273     if (as->parms[2].items) 
2274         temp |= 1;      /* set fast flag */
2275     if (as->parms[1].items) 
2276         temp &= ~2;     /* turn off local cell check */
2277     
2278     checkserv.magic = 0x12345678;       /* XXX */
2279     checkserv.tflags=temp;
2280
2281     /* now copy in optional cell name, if specified */
2282     if (as->parms[0].items) {
2283         code = GetCellName(as->parms[0].items->data, &info);
2284         if (code) {
2285             return 1;
2286         }
2287         strcpy(checkserv.tbuffer,info.name);
2288         checkserv.tsize=(int)strlen(info.name)+1;
2289         if (info.linkedCell)
2290             free(info.linkedCell);
2291     } else {
2292         strcpy(checkserv.tbuffer,"\0");
2293         checkserv.tsize=0;
2294     }
2295
2296     if(as->parms[3].items) {
2297         checkserv.tinterval=atol(as->parms[3].items->data);
2298
2299         /* sanity check */
2300         if(checkserv.tinterval<0) {
2301             printf("Warning: The negative -interval is ignored; treated as an inquiry\n");
2302             checkserv.tinterval=-1;
2303         } else if(checkserv.tinterval> 600) {
2304             printf("Warning: The maximum -interval value is 10 mins (600 secs)\n");
2305             checkserv.tinterval=600;    /* 10 min max interval */
2306         }       
2307     } else {
2308         checkserv.tinterval = -1;       /* don't change current interval */
2309     }
2310
2311     if ( checkserv.tinterval >= 0 ) {
2312 #ifdef WIN32
2313         if ( !IsAdmin() ) {
2314             fprintf (stderr,"Permission denied: requires AFS Client Administrator access.\n");
2315             return EACCES;
2316         }
2317 #else /* WIN32 */
2318         if (geteuid()) {
2319             fprintf (stderr,"Permission denied: requires root access.\n");
2320             return EACCES;
2321         }
2322 #endif /* WIN32 */
2323     }
2324
2325     code = pioctl_utf8(0, VIOCCKSERV, &blob, 1);
2326     if (code) {
2327         if ((errno == EACCES) && (checkserv.tinterval > 0)) {
2328             printf("Must be root to change -interval\n");
2329             return code;
2330         }
2331         Die(errno, 0);
2332         return 1;
2333     }
2334     memcpy(&temp, space, sizeof(afs_int32));
2335     if (checkserv.tinterval >= 0) {
2336         if (checkserv.tinterval > 0) 
2337             printf("The new down server probe interval (%d secs) is now in effect (old interval was %d secs)\n", 
2338                    checkserv.tinterval, temp);
2339         else 
2340             printf("The current down server probe interval is %d secs\n", temp);
2341         return 0;
2342     }
2343     if (temp == 0) {
2344         printf("All servers are running.\n");
2345     } else {
2346         printf("These servers unavailable due to network or server problems: ");
2347         for(j=0; j < MAXHOSTS; j++) {
2348             memcpy(&temp, space + j*sizeof(afs_int32), sizeof(afs_int32));
2349             if (temp == 0) 
2350                 break;
2351             tp = hostutil_GetNameByINet(temp);
2352             printf(" %s", tp);
2353         }
2354         printf(".\n");
2355         code = 1;       /* XXX */
2356     }
2357     return code;
2358 }
2359
2360 static int
2361 MessagesCmd(struct cmd_syndesc *as, void *arock)
2362 {
2363     afs_int32 code=0;
2364     struct ViceIoctl blob;
2365     struct gaginfo gagflags;
2366     struct cmd_item *show;
2367     
2368     memset(&gagflags, 0, sizeof(struct gaginfo));
2369     blob.in_size = sizeof(struct gaginfo);
2370     blob.in = (caddr_t ) &gagflags;
2371     blob.out_size = MAXSIZE;
2372     blob.out = space;
2373     memset(space, 0, sizeof(afs_int32));        /* so we assure zero when nothing is copied back */
2374
2375     if (show = as->parms[0].items) {
2376         if (!strcasecmp (show->data, "user"))
2377             gagflags.showflags |= GAGUSER;
2378         else if (!strcasecmp (show->data, "console"))
2379             gagflags.showflags |= GAGCONSOLE;
2380         else if (!strcasecmp (show->data, "all"))
2381             gagflags.showflags |= GAGCONSOLE | GAGUSER;
2382         else if (!strcasecmp (show->data, "none"))
2383             /* do nothing */ ;
2384         else {
2385             fprintf(stderr, 
2386                      "unrecognized flag %s: must be in {user,console,all,none}\n", 
2387                      show->data);
2388             code = EINVAL;
2389         }
2390     }
2391  
2392     if (code)
2393         return 1;
2394
2395     code = pioctl_utf8(0, VIOC_GAG, &blob, 1);
2396     if (code) {
2397         Die(errno, 0);
2398         return 1;
2399     }
2400     return 0;
2401 }
2402
2403 static int
2404 CheckVolumesCmd(struct cmd_syndesc *as, void *arock)
2405 {
2406     afs_int32 code;
2407     struct ViceIoctl blob;
2408     
2409     blob.in_size = 0;
2410     blob.out_size = 0;
2411     code = pioctl_utf8(0, VIOCCKBACK, &blob, 1);
2412     if (code) {
2413         Die(errno, 0);
2414         return 1;
2415     }
2416     printf("All volumeID/name mappings checked.\n");
2417     
2418     return 0;
2419 }
2420
2421 static int
2422 SetCacheSizeCmd(struct cmd_syndesc *as, void *arock)
2423 {
2424     afs_int32 code;
2425     struct ViceIoctl blob;
2426     afs_int32 temp;
2427     
2428 #ifdef WIN32
2429     if ( !IsAdmin() ) {
2430         fprintf (stderr,"Permission denied: requires AFS Client Administrator access.\n");
2431         return EACCES;
2432     }
2433 #else /* WIN32 */
2434     if (geteuid()) {
2435         fprintf (stderr,"Permission denied: requires root access.\n");
2436         return EACCES;
2437     }
2438 #endif /* WIN32 */
2439
2440     if (!as->parms[0].items && !as->parms[1].items) {
2441         fprintf(stderr,"%s: syntax error in set cache size cmd.\n", pn);
2442         return 1;
2443     }
2444     if (as->parms[0].items) {
2445         code = util_GetInt32(as->parms[0].items->data, &temp);
2446         if (code) {
2447             fprintf(stderr,"%s: bad integer specified for cache size.\n", pn);
2448             return 1;
2449         }
2450     } else
2451         temp = 0;
2452     blob.in = (char *) &temp;
2453     blob.in_size = sizeof(afs_int32);
2454     blob.out_size = 0;
2455     code = pioctl_utf8(0, VIOCSETCACHESIZE, &blob, 1);
2456     if (code) {
2457         Die(errno, (char *) 0);
2458         return 1;
2459     } 
2460       
2461     printf("New cache size set.\n");
2462     return 0;
2463 }
2464
2465 static int
2466 GetCacheParmsCmd(struct cmd_syndesc *as, void *arock)
2467 {
2468     afs_int32 code;
2469     struct ViceIoctl blob;
2470     cm_cacheParms_t parms;
2471
2472     memset(&parms, 0, sizeof(parms));
2473     blob.in = NULL;
2474     blob.in_size = 0;
2475     blob.out_size = sizeof(parms);
2476     blob.out = (char *) &parms;
2477     code = pioctl_utf8(0, VIOCGETCACHEPARMS, &blob, 1);
2478     if (code) {
2479         Die(errno, NULL);
2480         return 1;
2481     }
2482      
2483     printf("AFS using %I64u of the cache's available %I64u 1K byte blocks.\n",
2484            parms.parms[1], parms.parms[0]);
2485     if (parms.parms[1] > parms.parms[0])
2486         printf("[Cache guideline temporarily deliberately exceeded; it will be adjusted down but you may wish to increase the cache size.]\n");
2487     return 0;
2488 }
2489
2490 static int
2491 ListCellsCmd(struct cmd_syndesc *as, void *arock)
2492 {
2493     afs_int32 code;
2494     afs_int32 i, j, *lp, magic, size;
2495     char *tp;
2496     afs_int32 addr, maxa = OMAXHOSTS;
2497     struct ViceIoctl blob;
2498     int resolve;
2499
2500     resolve = !(as->parms[0].items);    /* -numeric */
2501     
2502     for(i=0;i<1000;i++) {
2503         tp = space;
2504         memcpy(tp, &i, sizeof(afs_int32));
2505         tp = (char *)(space + sizeof(afs_int32));
2506         lp = (afs_int32 *)tp;
2507         *lp++ = 0x12345678;
2508         size = sizeof(afs_int32) + sizeof(afs_int32);
2509         blob.out_size = MAXSIZE;
2510         blob.in_size = sizeof(afs_int32);
2511         blob.in = space;
2512         blob.out = space;
2513         code = pioctl_utf8(0, VIOCGETCELL, &blob, 1);
2514         if (code < 0) {
2515             if (errno == EDOM) 
2516                 break;  /* done with the list */
2517             Die(errno, 0);
2518             return 1;
2519         }       
2520         tp = space;
2521         memcpy(&magic, tp, sizeof(afs_int32));  
2522         if (magic == 0x12345678) {
2523             maxa = MAXHOSTS;
2524             tp += sizeof(afs_int32);
2525         }
2526         printf("Cell %s on hosts", tp+maxa*sizeof(afs_int32));
2527         for(j=0; j < maxa; j++) {
2528             char *name, tbuffer[20];
2529
2530             memcpy(&addr, tp + j*sizeof(afs_int32), sizeof(afs_int32));
2531             if (addr == 0) 
2532                 break;
2533
2534             if (resolve) {
2535                 name = hostutil_GetNameByINet(addr);
2536             } else {
2537                 addr = ntohl(addr);
2538                 sprintf(tbuffer, "%d.%d.%d.%d", (addr >> 24) & 0xff,
2539                          (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff);
2540                 name = tbuffer;
2541             }
2542             printf(" %s", name);
2543         }
2544         printf(".\n");
2545     }
2546     return 0;
2547 }
2548
2549 #ifndef WIN32
2550 static int
2551 ListAliasesCmd(struct cmd_syndesc *as, void *arock)
2552 {
2553     afs_int32 code, i;
2554     char *tp, *aliasName, *realName;
2555     struct ViceIoctl blob;
2556
2557     for (i = 0;; i++) {
2558         tp = space;
2559         memcpy(tp, &i, sizeof(afs_int32));
2560         blob.out_size = MAXSIZE;
2561         blob.in_size = sizeof(afs_int32);
2562         blob.in = space;
2563         blob.out = space;
2564         code = pioctl_utf8(0, VIOC_GETALIAS, &blob, 1);
2565         if (code < 0) {
2566             if (errno == EDOM)
2567                 break;          /* done with the list */
2568             Die(errno, 0);
2569             return 1;
2570         }
2571         tp = space;
2572         aliasName = tp;
2573         tp += strlen(aliasName) + 1;
2574         realName = tp;
2575         printf("Alias %s for cell %s\n", aliasName, realName);
2576     }
2577     return 0;
2578 }
2579
2580 static int
2581 CallBackRxConnCmd(struct cmd_syndesc *as, void *arock)
2582 {
2583     afs_int32 code;
2584     struct ViceIoctl blob;
2585     struct cmd_item *ti;
2586     afs_int32 hostAddr;
2587     struct hostent *thp;
2588     char *tp;
2589     int setp;
2590     
2591     ti = as->parms[0].items;
2592     setp = 1;
2593     if (ti) {
2594         thp = hostutil_GetHostByName(ti->data);
2595         if (!thp) {
2596             fprintf(stderr, "host %s not found in host table.\n", ti->data);
2597             return 1;
2598         }
2599         else memcpy(&hostAddr, thp->h_addr, sizeof(afs_int32));
2600     } else {
2601         hostAddr = 0;   /* means don't set host */
2602         setp = 0;       /* aren't setting host */
2603     }
2604     
2605     /* now do operation */
2606     blob.in_size = sizeof(afs_int32);
2607     blob.out_size = sizeof(afs_int32);
2608     blob.in = (char *) &hostAddr;
2609     blob.out = (char *) &hostAddr;
2610     
2611     code = pioctl_utf8(0, VIOC_CBADDR, &blob, 1);
2612     if (code < 0) {
2613         Die(errno, 0);
2614         return 1;
2615     }
2616     return 0;
2617 }
2618 #endif /* WIN32 */
2619
2620 static int
2621 NewCellCmd(struct cmd_syndesc *as, void *arock)
2622 {
2623 #ifndef WIN32
2624     afs_int32 code, linkedstate=0, size=0, *lp;
2625     struct ViceIoctl blob;
2626     struct cmd_item *ti;
2627     char *tp, *cellname=0;
2628     struct hostent *thp;
2629     afs_int32 fsport = 0, vlport = 0;
2630
2631     memset(space, 0, MAXHOSTS * sizeof(afs_int32));
2632     tp = space;
2633     lp = (afs_int32 *)tp;
2634     *lp++ = 0x12345678;
2635     tp += sizeof(afs_int32);
2636     for(ti=as->parms[1].items; ti; ti=ti->next) {
2637         thp = hostutil_GetHostByName(ti->data);
2638         if (!thp) {
2639             fprintf(stderr,"%s: Host %s not found in host table, skipping it.\n",
2640                    pn, ti->data);
2641         }
2642         else {
2643             memcpy(tp, thp->h_addr, sizeof(afs_int32));
2644             tp += sizeof(afs_int32);
2645         }
2646     }
2647     if (as->parms[2].items) {
2648         /*
2649          * Link the cell, for the purposes of volume location, to the specified
2650          * cell.
2651          */
2652         cellname = as->parms[2].items->data;
2653         linkedstate = 1;
2654     }
2655 #ifdef FS_ENABLE_SERVER_DEBUG_PORTS
2656     if (as->parms[3].items) {
2657         code = util_GetInt32(as->parms[3].items->data, &vlport);
2658         if (code) {
2659             fprintf(stderr,"fs: bad integer specified for the fileserver port.\n");
2660             return code;
2661         }
2662     }
2663     if (as->parms[4].items) {
2664         code = util_GetInt32(as->parms[4].items->data, &fsport);
2665         if (code) {
2666             fprintf(stderr,"fs: bad integer specified for the vldb server port.\n");
2667             return code;
2668         }
2669     }
2670 #endif
2671     tp = (char *)(space + (MAXHOSTS+1) *sizeof(afs_int32));
2672     lp = (afs_int32 *)tp;    
2673     *lp++ = fsport;
2674     *lp++ = vlport;
2675     *lp = linkedstate;
2676     strcpy(space +  ((MAXHOSTS+4) * sizeof(afs_int32)), as->parms[0].items->data);
2677     size = ((MAXHOSTS+4) * sizeof(afs_int32)) + strlen(as->parms[0].items->data) + 1 /* for null */;
2678     tp = (char *)(space + size);
2679     if (linkedstate) {
2680         strcpy(tp, cellname);
2681         size += strlen(cellname) + 1;
2682     }
2683     blob.in_size = size;
2684     blob.in = space;
2685     blob.out_size = 0;
2686     code = pioctl_utf8(0, VIOCNEWCELL, &blob, 1);
2687     if (code < 0)
2688         Die(errno, 0);
2689     return 0;
2690 #else /* WIN32 */
2691     afs_int32 code;
2692     struct ViceIoctl blob;
2693     
2694     if ( !IsAdmin() ) {
2695         fprintf (stderr,"Permission denied: requires AFS Client Administrator access.\n");
2696         return EACCES;
2697     }
2698
2699     blob.in_size = 0;
2700     blob.in = (char *) 0;
2701     blob.out_size = MAXSIZE;
2702     blob.out = space;
2703
2704     code = pioctl_utf8((char *) 0, VIOCNEWCELL, &blob, 1);
2705
2706     if (code) {
2707         Die(errno, (char *) 0);
2708         return 1;
2709     }
2710     
2711     printf("Cell servers information refreshed\n");
2712     return 0;
2713 #endif /* WIN32 */
2714 }
2715
2716 #ifndef WIN32
2717 static int
2718 NewAliasCmd(struct cmd_syndesc *as, void *arock)
2719 {
2720     afs_int32 code;
2721     struct ViceIoctl blob;
2722     char *tp;
2723     char *aliasName, *realName;
2724
2725     /* Setup and do the NEWALIAS pioctl call */
2726     aliasName = as->parms[0].items->data;
2727     realName = as->parms[1].items->data;
2728     tp = space;
2729     strcpy(tp, aliasName);
2730     tp += strlen(aliasName) + 1;
2731     strcpy(tp, realName);
2732     tp += strlen(realName) + 1;
2733
2734     blob.in_size = tp - space;
2735     blob.in = space;
2736     blob.out_size = 0;
2737     blob.out = space;
2738     code = pioctl_utf8(0, VIOC_NEWALIAS, &blob, 1);
2739     if (code < 0) {
2740         if (errno == EEXIST) {
2741             fprintf(stderr,
2742                     "%s: cell name `%s' in use by an existing cell.\n", pn,
2743                     aliasName);
2744         } else {
2745             Die(errno, 0);
2746         }
2747         return 1;
2748     }
2749     return 0;
2750 }
2751 #endif /* WIN32 */
2752
2753 static int
2754 WhichCellCmd(struct cmd_syndesc *as, void *arock)
2755 {
2756     afs_int32 code;
2757     struct cmd_item *ti;
2758     struct ViceIoctl blob;
2759     int error = 0;
2760     int literal = 0;
2761     cm_ioctlQueryOptions_t options;
2762
2763     if (as->parms[1].items)
2764         literal = 1;
2765     
2766     SetDotDefault(&as->parms[0].items);
2767     for(ti=as->parms[0].items; ti; ti=ti->next) {
2768         cm_fid_t fid;
2769         afs_uint32 filetype;
2770         char cell[CELL_MAXNAMELEN];
2771
2772         /* once per file */
2773         memset(&fid, 0, sizeof(fid));
2774         memset(&options, 0, sizeof(options));
2775         filetype = 0;
2776         options.size = sizeof(options);
2777         options.field_flags |= CM_IOCTL_QOPTS_FIELD_LITERAL;
2778         options.literal = literal;
2779         blob.in_size = options.size;    /* no variable length data */
2780         blob.in = &options;
2781
2782         blob.out_size = sizeof(cm_fid_t);
2783         blob.out = (char *) &fid;
2784         if (0 == pioctl_utf8(ti->data, VIOCGETFID, &blob, 1)) {
2785             options.field_flags |= CM_IOCTL_QOPTS_FIELD_FID;
2786             options.fid = fid;
2787         } else {
2788             Die(errno, ti->data);
2789             error = 1;
2790             continue;
2791         }
2792
2793         blob.out_size = sizeof(filetype);
2794         blob.out = &filetype;
2795
2796         code = pioctl_utf8(ti->data, VIOC_GETFILETYPE, &blob, 1);
2797
2798         blob.out_size = CELL_MAXNAMELEN;
2799         blob.out = cell;
2800
2801         code = pioctl_utf8(ti->data, VIOC_FILE_CELL_NAME, &blob, 1);
2802         if (code) {
2803             if (errno == ENOENT)
2804                 fprintf(stderr,"%s: no such cell as '%s'\n", pn, ti->data);
2805             else
2806                 Die(errno, ti->data);
2807             error = 1;
2808             continue;
2809         }
2810         printf("%s %s lives in cell '%s'\n",
2811                 filetypestr(filetype),
2812                 ti->data, cell);
2813     }
2814     return error;
2815 }
2816
2817 static int
2818 WSCellCmd(struct cmd_syndesc *as, void *arock)
2819 {
2820     afs_int32 code;
2821     struct ViceIoctl blob;
2822     
2823     blob.in_size = 0;
2824     blob.in = NULL;
2825     blob.out_size = MAXSIZE;
2826     blob.out = space;
2827
2828     code = pioctl_utf8(NULL, VIOC_GET_WS_CELL, &blob, 1);
2829
2830     if (code) {
2831         Die(errno, NULL);
2832         return 1;
2833     }
2834
2835     printf("This workstation belongs to cell '%s'\n", space);
2836     return 0;
2837 }
2838
2839 /*
2840 static int
2841 PrimaryCellCmd(struct cmd_syndesc *as, void *arock)
2842 {
2843     fprintf(stderr,"This command is obsolete, as is the concept of a primary token.\n");
2844     return 0;
2845 }
2846 */
2847
2848 #ifndef AFS_NT40_ENV
2849 static int
2850 MonitorCmd(struct cmd_syndesc *as, void *arock)
2851 {
2852     afs_int32 code;
2853     struct ViceIoctl blob;
2854     struct cmd_item *ti;
2855     afs_int32 hostAddr;
2856     struct hostent *thp;
2857     char *tp;
2858     int setp;
2859     
2860     ti = as->parms[0].items;
2861     setp = 1;
2862     if (ti) {
2863         /* set the host */
2864         if (!strcmp(ti->data, "off")) {
2865             hostAddr = 0xffffffff;
2866         } else {
2867             thp = hostutil_GetHostByName(ti->data);
2868             if (!thp) {
2869                 if (!strcmp(ti->data, "localhost")) {
2870                     fprintf(stderr,"localhost not in host table, assuming 127.0.0.1\n");
2871                     hostAddr = htonl(0x7f000001);
2872                 } else {
2873                     fprintf(stderr,"host %s not found in host table.\n", ti->data);
2874                     return 1;
2875                 }
2876             } else {
2877                 memcpy(&hostAddr, thp->h_addr, sizeof(afs_int32));
2878             }
2879         }
2880     } else {
2881         hostAddr = 0;   /* means don't set host */
2882         setp = 0;       /* aren't setting host */
2883     }
2884
2885     /* now do operation */
2886     blob.in_size = sizeof(afs_int32);
2887     blob.out_size = sizeof(afs_int32);
2888     blob.in = (char *) &hostAddr;
2889     blob.out = (char *) &hostAddr;
2890     code = pioctl_utf8(0, VIOC_AFS_MARINER_HOST, &blob, 1);
2891     if (code) {
2892         Die(errno, 0);
2893         return 1;
2894     }
2895     if (setp) {
2896         printf("%s: new monitor host set.\n", pn);
2897     } else {
2898         /* now decode old address */
2899         if (hostAddr == 0xffffffff) {
2900             printf("Cache monitoring is currently disabled.\n");
2901         } else {
2902             tp = hostutil_GetNameByINet(hostAddr);
2903             printf("Using host %s for monitor services.\n", tp);
2904         }
2905     }
2906     return 0;
2907 }
2908 #endif /* AFS_NT40_ENV */
2909
2910 static int
2911 SysNameCmd(struct cmd_syndesc *as, void *arock)
2912 {
2913     afs_int32 code;
2914     struct ViceIoctl blob;
2915     struct cmd_item *ti;
2916     char *input = space;
2917     afs_int32 setp = 0;
2918     
2919     ti = as->parms[0].items;
2920     if (ti) {
2921 #ifdef WIN32
2922     if ( !IsAdmin() ) {
2923         fprintf (stderr,"Permission denied: requires AFS Client Administrator access.\n");
2924         return EACCES;
2925     }
2926 #else /* WIN32 */
2927     if (geteuid()) {
2928         fprintf (stderr,"Permission denied: requires root access.\n");
2929         return EACCES;
2930     }
2931 #endif /* WIN32 */
2932     }
2933
2934     blob.in = space;
2935     blob.out = space;
2936     blob.out_size = MAXSIZE;
2937     blob.in_size = sizeof(afs_int32);
2938     memcpy(input, &setp, sizeof(afs_int32));
2939     input += sizeof(afs_int32);
2940     for (; ti; ti = ti->next) {
2941         setp++;
2942         blob.in_size += (long)strlen(ti->data) + 1;
2943         if (blob.in_size > MAXSIZE) {
2944             fprintf(stderr, "%s: sysname%s too long.\n", pn,
2945                      setp > 1 ? "s" : "");
2946             return 1;
2947         }
2948         strcpy(input, ti->data);
2949         input += strlen(ti->data);
2950         *(input++) = '\0';
2951     }
2952     memcpy(space, &setp, sizeof(afs_int32));
2953     code = pioctl_utf8(0, VIOC_AFS_SYSNAME, &blob, 1);
2954     if (code) {
2955         Die(errno, 0);
2956         return 1;
2957     }    
2958     if (setp) {
2959         printf("%s: new sysname%s set.\n", pn, setp > 1 ? " list" : "");
2960         return 0;
2961     }
2962
2963     input = space;
2964     memcpy(&setp, input, sizeof(afs_int32));
2965     input += sizeof(afs_int32);
2966     if (!setp) {
2967         fprintf(stderr,"No sysname name value was found\n");
2968         return 1;
2969     } 
2970     
2971     printf("Current sysname%s is", setp > 1 ? " list" : "");
2972     for (; setp > 0; --setp ) {
2973         printf(" \'%s\'", input);
2974         input += strlen(input) + 1;
2975     }
2976     printf("\n");
2977     return 0;
2978 }
2979
2980 #ifndef AFS_NT40_ENV
2981 static char *exported_types[] = {"null", "nfs", ""};
2982 static int ExportAfsCmd(struct cmd_syndesc *as, void *arock)
2983 {
2984     afs_int32 code;
2985     struct ViceIoctl blob;
2986     struct cmd_item *ti;
2987     int export = 0, type = 0, mode = 0, exp = 0, gstat = 0;
2988     int exportcall, pwsync = 0, smounts = 0;
2989     
2990 #ifdef WIN32
2991     if ( !IsAdmin() ) {
2992         fprintf (stderr,"Permission denied: requires AFS Client Administrator access.\n");
2993         return EACCES;
2994     }
2995 #else /* WIN32 */
2996     if (geteuid()) {
2997         fprintf (stderr,"Permission denied: requires root access.\n");
2998         return EACCES;
2999     }
3000 #endif /* WIN32 */
3001
3002     ti = as->parms[0].items;
3003     if (strcmp(ti->data, "nfs") == 0) 
3004         type = 0x71; /* NFS */
3005     else {
3006         fprintf(stderr,
3007                 "Invalid exporter type, '%s', Only the 'nfs' exporter is currently supported\n", ti->data);
3008         return 1;
3009     }
3010     ti = as->parms[1].items;
3011     if (ti) {
3012         if (strcmp(ti->data, "on") == 0) 
3013             export = 3;
3014         else if (strcmp(ti->data, "off") == 0) 
3015             export = 2;
3016         else {
3017             fprintf(stderr, "Illegal argument %s\n", ti->data);
3018             return 1;
3019         }
3020         exp = 1;
3021     }
3022     if (ti = as->parms[2].items) {      /* -noconvert */
3023         if (strcmp(ti->data, "on") == 0) 
3024             mode = 2;
3025         else if (strcmp(ti->data, "off") == 0) 
3026             mode = 3;
3027         else {
3028             fprintf(stderr, "Illegal argument %s\n", ti->data);
3029             return 1;
3030         }
3031     }
3032     if (ti = as->parms[3].items) {      /* -uidcheck */
3033         if (strcmp(ti->data, "on") == 0) 
3034             pwsync = 3;
3035         else if (strcmp(ti->data, "off") == 0) 
3036             pwsync = 2;
3037         else {
3038             fprintf(stderr, "Illegal argument %s\n", ti->data);
3039             return 1;
3040         }
3041     }
3042     if (ti = as->parms[4].items) {      /* -submounts */
3043         if (strcmp(ti->data, "on") == 0) 
3044             smounts = 3;
3045         else if (strcmp(ti->data, "off") == 0) 
3046             smounts = 2;
3047         else {
3048             fprintf(stderr, "Illegal argument %s\n", ti->data);
3049             return 1;
3050         }
3051     }
3052     exportcall =  (type << 24) | (mode << 6) | (pwsync << 4) | (smounts << 2) | export;
3053     type &= ~0x70;
3054     /* make the call */
3055     blob.in = (char *) &exportcall;
3056     blob.in_size = sizeof(afs_int32);
3057     blob.out = (char *) &exportcall;
3058     blob.out_size = sizeof(afs_int32);
3059     code = pioctl_utf8(0, VIOC_EXPORTAFS, &blob, 1);
3060     if (code) {
3061         if (errno == ENODEV) {
3062             fprintf(stderr,
3063                     "Sorry, the %s-exporter type is currently not supported on this AFS client\n", exported_types[type]);
3064         } else {
3065             Die(errno, 0);
3066         }
3067         return 1;
3068     } else {
3069         if (!gstat) {
3070             if (exportcall & 1) {
3071                 printf("'%s' translator is enabled with the following options:\n\tRunning in %s mode\n\tRunning in %s mode\n\t%s\n", 
3072                        exported_types[type], (exportcall & 2 ? "strict unix" : "convert owner mode bits to world/other"),
3073                        (exportcall & 4 ? "strict 'passwd sync'" : "no 'passwd sync'"),
3074                        (exportcall & 8 ? "Allow mounts of /afs/.. subdirs" : "Only mounts to /afs allowed"));
3075             } else {
3076                 printf("'%s' translator is disabled\n", exported_types[type]);
3077             }
3078         }
3079     }
3080     return 0;
3081 }
3082 #endif
3083
3084 static int
3085 GetCellCmd(struct cmd_syndesc *as, void *arock)
3086 {
3087     afs_int32 code;
3088     struct ViceIoctl blob;
3089     struct afsconf_cell info;
3090     struct cmd_item *ti;
3091     struct a {
3092         afs_int32 stat;
3093         afs_int32 junk;
3094     } args;
3095     int error = 0;
3096
3097     memset(&info, 0, sizeof(info));
3098     memset(&args, 0, sizeof(args));      /* avoid Purify UMR error */
3099     for(ti=as->parms[0].items; ti; ti=ti->next) {
3100         /* once per cell */
3101         blob.out_size = sizeof(args);
3102         blob.out = (caddr_t) &args;
3103         code = GetCellName(ti->data, &info);
3104         if (code) {
3105             error = 1;
3106             continue;
3107         }
3108         if (info.linkedCell)
3109             free(info.linkedCell);
3110         blob.in_size = 1+(long)strlen(info.name);
3111         blob.in = info.name;
3112         code = pioctl_utf8(0, VIOC_GETCELLSTATUS, &blob, 1);
3113         if (code) {
3114             if (errno == ENOENT)
3115                 fprintf(stderr,"%s: the cell named '%s' does not exist\n", pn, info.name);
3116             else
3117                 Die(errno, info.name);
3118             error = 1;
3119             continue;
3120         }
3121         printf("Cell %s status: ", info.name);
3122 #ifdef notdef
3123         if (args.stat & 1) 
3124             printf("primary ");
3125 #endif
3126         if (args.stat & 2) 
3127             printf("no setuid allowed");
3128         else 
3129             printf("setuid allowed");
3130         if (args.stat & 4) 
3131             printf(", using old VLDB");
3132         printf("\n");
3133     }
3134     return error;
3135 }
3136
3137 static int SetCellCmd(struct cmd_syndesc *as, void *arock)
3138 {
3139     afs_int32 code;
3140     struct ViceIoctl blob;
3141     struct afsconf_cell info;
3142     struct cmd_item *ti;
3143     struct a {
3144         afs_int32 stat;
3145         afs_int32 junk;
3146         char cname[64];
3147     } args;
3148     int error = 0;
3149
3150     memset(&info, 0, sizeof(info));
3151
3152     /* Check arguments. */
3153     if (as->parms[1].items && as->parms[2].items) {
3154         fprintf(stderr, "Cannot specify both -suid and -nosuid.\n");
3155         return 1;
3156     }
3157
3158     /* figure stuff to set */
3159     args.stat = 0;
3160     args.junk = 0;
3161
3162 #ifdef WIN32
3163     if ( !IsAdmin() ) {
3164         fprintf (stderr,"Permission denied: requires AFS Client Administrator access.\n");
3165         return EACCES;
3166     }
3167 #else /* WIN32 */
3168     if (geteuid()) {
3169         fprintf (stderr,"Permission denied: requires root access.\n");
3170         return EACCES;
3171     }
3172 #endif /* WIN32 */
3173
3174     if (! as->parms[1].items) 
3175         args.stat |= CM_SETCELLFLAG_SUID; /* default to -nosuid */
3176
3177     /* set stat for all listed cells */
3178     for(ti=as->parms[0].items; ti; ti=ti->next) {
3179         /* once per cell */
3180         code = GetCellName(ti->data, &info);
3181         if (code) {
3182             error = 1;
3183             continue;
3184         }
3185         if (info.linkedCell)
3186             free(info.linkedCell);
3187         strcpy(args.cname, info.name);
3188         blob.in_size = sizeof(args);
3189         blob.in = (caddr_t) &args;
3190         blob.out_size = 0;
3191         blob.out = (caddr_t) 0;
3192         code = pioctl_utf8(0, VIOC_SETCELLSTATUS, &blob, 1);
3193         if (code) {
3194             Die(errno, info.name);      /* XXX added cell name to Die() call */
3195             error = 1;
3196         }
3197     }
3198     return error;
3199 }
3200
3201 static int
3202 GetCellName(char *cellNamep, struct afsconf_cell *infop)
3203 {
3204     strcpy(infop->name, cellNamep);
3205     return 0;
3206 }
3207
3208 static int
3209 VLDBInit(int noAuthFlag, struct afsconf_cell *info)
3210 {
3211     afs_int32 code;
3212     char confDir[257];
3213
3214     cm_GetConfigDir(confDir, sizeof(confDir));
3215
3216     code = ugen_ClientInit(noAuthFlag, confDir, 
3217                            info->name, 0, &uclient, 
3218                            NULL, pn, rxkad_clear,
3219                            VLDB_MAXSERVERS, AFSCONF_VLDBSERVICE, 50,
3220                            0, 0, USER_SERVICE_ID);
3221     rxInitDone = 1;
3222     return code;
3223 }
3224
3225 static struct ViceIoctl gblob;
3226 static int debug = 0;
3227 /* 
3228  * here follow some routines in suport of the setserverprefs and
3229  * getserverprefs commands.  They are:
3230  * SetPrefCmd  "top-level" routine
3231  * addServer   adds a server to the list of servers to be poked into the
3232  *             kernel.  Will poke the list into the kernel if it threatens
3233  *             to get too large.
3234  * pokeServers pokes the existing list of servers and ranks into the kernel
3235  * GetPrefCmd  reads the Cache Manager's current list of server ranks
3236  */
3237
3238 #ifdef WIN32
3239 static int 
3240 pokeServers(void)
3241 {
3242     int code;
3243     cm_SSetPref_t *ssp;
3244     code = pioctl_utf8(0, VIOC_SETSPREFS, &gblob, 1);
3245
3246     ssp = (cm_SSetPref_t *)space;
3247     gblob.in_size = (long)(((char *)&(ssp->servers[0])) - (char *)ssp);
3248     gblob.in = space;
3249     return code;
3250 }
3251 #else
3252 /*
3253  * returns -1 if error message printed,
3254  * 0 on success,
3255  * errno value if error and no error message printed
3256  */
3257 static int
3258 pokeServers(void)
3259 {
3260     int code;
3261
3262     code = pioctl_utf8(0, VIOC_SETSPREFS, &gblob, 1);
3263     if (code && (errno == EINVAL)) {
3264         struct setspref *ssp;
3265         ssp = (struct setspref *)gblob.in;
3266         if (!(ssp->flags & DBservers)) {
3267             gblob.in = (void *)&(ssp->servers[0]);
3268             gblob.in_size -= ((char *)&(ssp->servers[0])) - (char *)ssp;
3269             code = pioctl_utf8(0, VIOC_SETSPREFS33, &gblob, 1);
3270             return code ? errno : 0;
3271         }
3272         fprintf(stderr,
3273                 "This cache manager does not support VL server preferences.\n");
3274         return -1;
3275     }
3276
3277     return code ? errno : 0;
3278 }
3279 #endif /* WIN32 */
3280
3281 #ifdef WIN32
3282 static int
3283 addServer(char *name, unsigned short rank)
3284 {  
3285     int code;
3286     cm_SSetPref_t *ssp;
3287     cm_SPref_t *sp;
3288     struct hostent *thostent;
3289
3290 #ifndef MAXUSHORT
3291 #ifdef MAXSHORT
3292 #define MAXUSHORT ((unsigned short) 2*MAXSHORT+1)  /* assumes two's complement binary system */
3293 #else
3294 #define MAXUSHORT ((unsigned short) ~0)
3295 #endif
3296 #endif
3297
3298     code = 0;
3299     thostent = hostutil_GetHostByName(name);
3300     if (!thostent) {
3301         fprintf (stderr, "%s: couldn't resolve name.\n", name);
3302         return EINVAL;
3303     }
3304
3305     ssp = (cm_SSetPref_t *)(gblob.in);
3306
3307     if (gblob.in_size > MAXINSIZE - sizeof(cm_SPref_t)) {
3308         code = pokeServers();
3309         ssp->num_servers = 0;
3310     }
3311
3312     sp = (cm_SPref_t *)((char*)gblob.in + gblob.in_size);
3313     memcpy (&(sp->host.s_addr), thostent->h_addr, sizeof(afs_uint32));
3314     sp->rank = (rank > MAXUSHORT ? MAXUSHORT : rank);
3315     gblob.in_size += sizeof(cm_SPref_t);
3316     ssp->num_servers++;
3317
3318     if (debug) fprintf(stderr, "adding server %s, rank %d, ip addr 0x%lx\n",name,sp->rank,sp->host.s_addr);
3319
3320     return code;
3321 }
3322 #else
3323 /*
3324  * returns -1 if error message printed,
3325  * 0 on success,
3326  * errno value if error and no error message printed
3327  */
3328 static int
3329 addServer(char *name, afs_int32 rank)
3330 {
3331     int t, code;
3332     struct setspref *ssp;
3333     struct spref *sp;
3334     struct hostent *thostent;
3335     afs_uint32 addr;
3336     int error = 0;
3337
3338 #ifndef MAXUSHORT
3339 #ifdef MAXSHORT
3340 #define MAXUSHORT ((unsigned short) 2*MAXSHORT+1)       /* assumes two's complement binary system */
3341 #else
3342 #define MAXUSHORT ((unsigned short) ~0)
3343 #endif
3344 #endif
3345
3346     thostent = hostutil_GetHostByName(name);
3347     if (!thostent) {
3348         fprintf(stderr, "%s: couldn't resolve name.\n", name);
3349         return -1;
3350     }
3351
3352     ssp = (struct setspref *)(gblob.in);
3353
3354     for (t = 0; thostent->h_addr_list[t]; t++) {
3355         if (gblob.in_size > MAXINSIZE - sizeof(struct spref)) {
3356             code = pokeServers();
3357             if (code)
3358                 error = code;
3359             ssp->num_servers = 0;
3360         }
3361
3362         sp = (struct spref *)(gblob.in + gblob.in_size);
3363         memcpy(&(sp->server.s_addr), thostent->h_addr_list[t],
3364                sizeof(afs_uint32));
3365         sp->rank = (rank > MAXUSHORT ? MAXUSHORT : rank);
3366         gblob.in_size += sizeof(struct spref);
3367         ssp->num_servers++;
3368
3369         if (debug)
3370             fprintf(stderr, "adding server %s, rank %d, ip addr 0x%lx\n",
3371                     name, sp->rank, sp->server.s_addr);
3372     }
3373
3374     return error;
3375 }
3376 #endif /* WIN32 */
3377
3378 #ifdef WIN32
3379 static BOOL IsWindowsNT (void)
3380 {
3381     static BOOL fChecked = FALSE;
3382     static BOOL fIsWinNT = FALSE;
3383
3384     if (!fChecked)
3385     {
3386         OSVERSIONINFO Version;
3387
3388         fChecked = TRUE;
3389
3390         memset (&Version, 0x00, sizeof(Version));
3391         Version.dwOSVersionInfoSize = sizeof(Version);
3392
3393         if (GetVersionEx (&Version))
3394         {
3395             if (Version.dwPlatformId == VER_PLATFORM_WIN32_NT)
3396                 fIsWinNT = TRUE;
3397         }
3398     }
3399     return fIsWinNT;
3400 }
3401 #endif /* WIN32 */
3402
3403 #ifdef WIN32
3404 static int
3405 SetPrefCmd(struct cmd_syndesc *as, void * arock)
3406 {
3407     FILE *infd;
3408     afs_int32 code;
3409     struct cmd_item *ti;
3410     char name[80];
3411     afs_int32 rank;
3412     cm_SSetPref_t *ssp;
3413     
3414     ssp = (cm_SSetPref_t *)space;
3415     ssp->flags = 0;
3416     ssp->num_servers = 0;
3417     gblob.in_size = (long)(((char*)&(ssp->servers[0])) - (char *)ssp);
3418     gblob.in = space;
3419     gblob.out = space;
3420     gblob.out_size = MAXSIZE;
3421
3422     if ( !IsAdmin() ) {
3423         fprintf (stderr,"Permission denied: requires AFS Client Administrator access.\n");
3424         return EACCES;
3425     }
3426
3427     code = 0;
3428
3429     ti = as->parms[2].items;  /* -file */
3430     if (ti) {
3431         if (debug) fprintf(stderr,"opening file %s\n",ti->data);
3432         if (!(infd = fopen(ti->data,"r" ))) {
3433             code = errno;
3434             Die(errno,ti->data);
3435         }
3436         else
3437             while ( fscanf(infd, "%79s%ld", name, &rank) != EOF) {
3438                 code = addServer (name, (unsigned short) rank);
3439             }
3440     }
3441
3442     ti = as->parms[3].items;  /* -stdin */
3443     if (ti) {
3444         while ( scanf("%79s%ld", name, &rank) != EOF) {
3445             code = addServer (name, (unsigned short) rank);
3446         }
3447     }
3448
3449     for (ti = as->parms[0].items;ti;ti=ti->next) {/*list of servers, ranks */
3450         if (ti) {
3451             if (!ti->next) {
3452                 break;
3453             }
3454             code = addServer (ti->data, (unsigned short) atol(ti->next->data));
3455             if (debug)
3456                 printf("set fs prefs %s %s\n", ti->data, ti->next->data);
3457             ti=ti->next;
3458         }
3459     }
3460     code = pokeServers();
3461     if (debug) 
3462         printf("now working on vlservers, code=%d, errno=%d\n",code,errno);
3463
3464     ssp = (cm_SSetPref_t *)space;
3465     gblob.in_size = (long)(((char*)&(ssp->servers[0])) - (char *)ssp);
3466     gblob.in = space;
3467     ssp->flags = CM_SPREF_VLONLY;
3468     ssp->num_servers = 0;
3469
3470     for (ti = as->parms[1].items;ti;ti=ti->next) { /* list of dbservers, ranks */
3471         if (ti) {
3472             if (!ti->next) {
3473                 break;
3474             }
3475             code = addServer (ti->data, (unsigned short) atol(ti->next->data));
3476             if (debug) 
3477                 printf("set vl prefs %s %s\n", ti->data, ti->next->data);
3478             ti=ti->next;
3479         }
3480     }
3481
3482     if (as->parms[1].items) {
3483         if (debug) 
3484             printf("now poking vlservers\n");
3485         code = pokeServers();
3486     }
3487
3488     if (code) 
3489         Die(errno,0);
3490
3491     return code;
3492 }
3493 #else
3494 static int
3495 SetPrefCmd(struct cmd_syndesc *as, void *arock)
3496 {
3497     FILE *infd;
3498     afs_int32 code;
3499     struct cmd_item *ti;
3500     char name[80];
3501     afs_int32 rank;
3502     struct setspref *ssp;
3503     int error = 0;              /* -1 means error message printed,
3504                                  * >0 means errno value for unprinted message */
3505
3506     ssp = (struct setspref *)space;
3507     ssp->flags = 0;
3508     ssp->num_servers = 0;
3509     gblob.in_size = ((char *)&(ssp->servers[0])) - (char *)ssp;
3510     gblob.in = space;
3511     gblob.out = space;
3512     gblob.out_size = MAXSIZE;
3513
3514
3515     if (geteuid()) {
3516         fprintf(stderr, "Permission denied: requires root access.\n");
3517         return 1;
3518     }
3519
3520     ti = as->parms[2].items;    /* -file */
3521     if (ti) {
3522         if (debug)
3523             fprintf(stderr, "opening file %s\n", ti->data);
3524         if (!(infd = fopen(ti->data, "r"))) {
3525             perror(ti->data);
3526             error = -1;
3527         } else {
3528             while (fscanf(infd, "%79s%ld", name, &rank) != EOF) {
3529                 code = addServer(name, (unsigned short)rank);
3530                 if (code)
3531                     error = code;
3532             }
3533         }
3534     }
3535
3536     ti = as->parms[3].items;    /* -stdin */
3537     if (ti) {
3538         while (scanf("%79s%ld", name, &rank) != EOF) {
3539             code = addServer(name, (unsigned short)rank);
3540             if (code)
3541                 error = code;
3542         }
3543     }
3544
3545     for (ti = as->parms[0].items; ti; ti = ti->next) {  /* list of servers, ranks */
3546         if (ti) {
3547             if (!ti->next) {
3548                 break;
3549             }
3550             code = addServer(ti->data, (unsigned short)atol(ti->next->data));
3551             if (code)
3552                 error = code;
3553             if (debug)
3554                 printf("set fs prefs %s %s\n", ti->data, ti->next->data);
3555             ti = ti->next;
3556         }
3557     }
3558     code = pokeServers();
3559     if (code)
3560         error = code;
3561     if (debug)
3562         printf("now working on vlservers, code=%d\n", code);
3563
3564     ssp = (struct setspref *)space;
3565     ssp->flags = DBservers;
3566     ssp->num_servers = 0;
3567     gblob.in_size = ((char *)&(ssp->servers[0])) - (char *)ssp;
3568     gblob.in = space;
3569
3570     for (ti = as->parms[1].items; ti; ti = ti->next) {  /* list of dbservers, ranks */
3571         if (ti) {
3572             if (!ti->next) {
3573                 break;
3574             }
3575             code = addServer(ti->data, (unsigned short)atol(ti->next->data));
3576             if (code)
3577                 error = code;
3578             if (debug)
3579                 printf("set vl prefs %s %s\n", ti->data, ti->next->data);
3580             ti = ti->next;
3581         }
3582     }
3583
3584     if (as->parms[1].items) {
3585         if (debug)
3586             printf("now poking vlservers\n");
3587         code = pokeServers();
3588         if (code)
3589             error = code;
3590     }
3591
3592     if (error > 0)
3593         Die(error, 0);
3594
3595     return error ? 1 : 0;
3596 }
3597 #endif /* WIN32 */
3598
3599 #ifdef WIN32
3600 static int 
3601 GetPrefCmd(struct cmd_syndesc *as, void *arock)
3602 {
3603     afs_int32 code;
3604     struct cmd_item *ti;
3605     char *name, tbuffer[20];
3606     afs_int32 addr;
3607     FILE * outfd;
3608     int resolve;
3609     int vlservers;
3610     struct ViceIoctl blob;
3611     struct cm_SPrefRequest *in;
3612     struct cm_SPrefInfo *out;
3613     int i;
3614     
3615     code = 0;
3616     ti = as->parms[0].items;  /* -file */
3617     if (ti) {
3618         if (debug) fprintf(stderr,"opening file %s\n",ti->data);
3619         if (!(outfd = freopen(ti->data,"w",stdout))) {
3620             Die(errno,ti->data);
3621             return errno;
3622         }
3623     }
3624
3625     ti = as->parms[1].items;  /* -numeric */
3626     resolve = !(ti);
3627     ti = as->parms[2].items;  /* -vlservers */
3628     vlservers = (ti ? CM_SPREF_VLONLY : 0);
3629     /*  ti = as->parms[3].items;   -cell */
3630
3631     in = (struct cm_SPrefRequest *)space;
3632     in->offset = 0;
3633
3634     do {
3635         blob.in_size=sizeof(struct cm_SPrefRequest);
3636         blob.in = (char *)in;
3637         blob.out = space;
3638         blob.out_size = MAXSIZE;
3639
3640         in->num_servers = (MAXSIZE - 2*sizeof(short))/sizeof(struct cm_SPref);
3641         in->flags = vlservers; 
3642
3643         code = pioctl_utf8(0, VIOC_GETSPREFS, &blob, 1);
3644         if (code){
3645             perror("getserverprefs pioctl");
3646             Die (errno,0);
3647         }
3648         else {
3649             out = (struct cm_SPrefInfo *) blob.out;
3650
3651             for (i=0;i<out->num_servers;i++) {
3652                 if (resolve) {
3653                     name = hostutil_GetNameByINet(out->servers[i].host.s_addr);
3654                 }
3655                 else {
3656                     addr = ntohl(out->servers[i].host.s_addr);
3657                     sprintf(tbuffer, "%d.%d.%d.%d", (addr>>24) & 0xff, (addr>>16) & 0xff,
3658                              (addr>>8) & 0xff, addr & 0xff);
3659                     name=tbuffer;
3660                 }
3661                 printf ("%-50s %5u\n",name,out->servers[i].rank);      
3662             }
3663
3664             in->offset = out->next_offset;
3665         }
3666     } while (!code && out->next_offset > 0);
3667
3668     return code;
3669 }
3670 #else
3671 static int
3672 GetPrefCmd(struct cmd_syndesc *as, void *arock)
3673 {
3674     afs_int32 code;
3675     struct cmd_item *ti;
3676     char *name, tbuffer[20];
3677     afs_int32 rank, addr;
3678     FILE *outfd;
3679     int resolve;
3680     int vlservers = 0;
3681     struct ViceIoctl blob;
3682     struct sprefrequest *in;
3683     struct sprefinfo *out;
3684     int i;
3685
3686     ti = as->parms[0].items;    /* -file */
3687     if (ti) {
3688         if (debug)
3689             fprintf(stderr, "opening file %s\n", ti->data);
3690         if (!(outfd = freopen(ti->data, "w", stdout))) {
3691             perror(ti->data);
3692             return 1;
3693         }
3694     }
3695
3696     ti = as->parms[1].items;    /* -numeric */
3697     resolve = !(ti);
3698     ti = as->parms[2].items;    /* -vlservers */
3699     vlservers |= (ti ? DBservers : 0);
3700     /*  ti = as->parms[3].items;   -cell */
3701
3702     in = (struct sprefrequest *)space;