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