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