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