a839b1769e0d3e9062c233522b954ad828953a17
[openafs.git] / src / volser / vos.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 #ifdef IGNORE_SOME_GCC_WARNINGS
14 # pragma GCC diagnostic warning "-Wimplicit-function-declaration"
15 #endif
16
17 #include <sys/types.h>
18 #include <string.h>
19 #ifdef HAVE_STDINT_H
20 # include <stdint.h>
21 #endif
22 #ifdef AFS_NT40_ENV
23 #include <fcntl.h>
24 #include <io.h>
25 #include <winsock2.h>
26 #include <WINNT/afsreg.h>
27 #else
28 #include <sys/time.h>
29 #include <sys/file.h>
30 #include <netdb.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #endif
34 #include <sys/stat.h>
35 #ifdef AFS_AIX_ENV
36 #include <sys/statfs.h>
37 #endif
38 #include <errno.h>
39
40 #include <lock.h>
41 #include <afs/stds.h>
42 #include <rx/xdr.h>
43 #include <rx/rx.h>
44 #include <rx/rx_globals.h>
45 #include <afs/nfs.h>
46 #include <afs/vlserver.h>
47 #include <afs/cellconfig.h>
48 #include <afs/keys.h>
49 #include <afs/afsutil.h>
50 #include <ubik.h>
51 #include <afs/afsint.h>
52 #include <afs/cmd.h>
53 #include <afs/usd.h>
54 #include "volser.h"
55 #include "volint.h"
56 #include <afs/ihandle.h>
57 #include <afs/vnode.h>
58 #include <afs/volume.h>
59 #include <afs/com_err.h>
60 #include "dump.h"
61 #include "lockdata.h"
62
63 #ifdef  AFS_AIX32_ENV
64 #include <signal.h>
65 #endif
66 #include "volser_internal.h"
67 #include "volser_prototypes.h"
68 #include "vsutils_prototypes.h"
69 #include "lockprocs_prototypes.h"
70
71 #ifdef HAVE_POSIX_REGEX
72 #include <regex.h>
73 #endif
74
75 /* Local Prototypes */
76 int PrintDiagnostics(char *astring, afs_int32 acode);
77 int GetVolumeInfo(afs_uint32 volid, afs_int32 *server, afs_int32 *part, 
78                   afs_int32 *voltype, struct nvldbentry *rentry);
79
80 struct tqElem {
81     afs_uint32 volid;
82     struct tqElem *next;
83 };
84
85 struct tqHead {
86     afs_int32 count;
87     struct tqElem *next;
88 };
89
90 #define COMMONPARMS     cmd_Seek(ts, 12);\
91 cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell name");\
92 cmd_AddParm(ts, "-noauth", CMD_FLAG, CMD_OPTIONAL, "don't authenticate");\
93 cmd_AddParm(ts, "-localauth",CMD_FLAG,CMD_OPTIONAL,"use server tickets");\
94 cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, "verbose");\
95 cmd_AddParm(ts, "-encrypt", CMD_FLAG, CMD_OPTIONAL, "encrypt commands");\
96 cmd_AddParm(ts, "-noresolve", CMD_FLAG, CMD_OPTIONAL, "don't resolve addresses"); \
97
98 #define ERROR_EXIT(code) {error=(code); goto error_exit;}
99
100 int rxInitDone = 0;
101 struct rx_connection *tconn;
102 afs_int32 tserver;
103 extern struct ubik_client *cstruct;
104 const char *confdir;
105
106 static struct tqHead busyHead, notokHead;
107
108 static void
109 qInit(struct tqHead *ahead)
110 {
111     memset(ahead, 0, sizeof(struct tqHead));
112     return;
113 }
114
115
116 static void
117 qPut(struct tqHead *ahead, afs_uint32 volid)
118 {
119     struct tqElem *elem;
120
121     elem = (struct tqElem *)malloc(sizeof(struct tqElem));
122     elem->next = ahead->next;
123     elem->volid = volid;
124     ahead->next = elem;
125     ahead->count++;
126     return;
127 }
128
129 static void
130 qGet(struct tqHead *ahead, afs_uint32 *volid)
131 {
132     struct tqElem *tmp;
133
134     if (ahead->count <= 0)
135         return;
136     *volid = ahead->next->volid;
137     tmp = ahead->next;
138     ahead->next = tmp->next;
139     ahead->count--;
140     free(tmp);
141     return;
142 }
143
144 /* returns 1 if <filename> exists else 0 */
145 static int
146 FileExists(char *filename)
147 {
148     usd_handle_t ufd;
149     int code;
150     afs_hyper_t size;
151
152     code = usd_Open(filename, USD_OPEN_RDONLY, 0, &ufd);
153     if (code) {
154         return 0;
155     }
156     code = USD_IOCTL(ufd, USD_IOCTL_GETSIZE, &size);
157     USD_CLOSE(ufd);
158     if (code) {
159         return 0;
160     }
161     return 1;
162 }
163
164 /* returns 1 if <name> doesnot end in .readonly or .backup, else 0 */
165 static int
166 VolNameOK(char *name)
167 {
168     int total;
169
170
171     total = strlen(name);
172     if (!strcmp(&name[total - 9], ".readonly")) {
173         return 0;
174     } else if (!strcmp(&name[total - 7], ".backup")) {
175         return 0;
176     } else {
177         return 1;
178     }
179 }
180
181 /* return 1 if name is a number else 0 */
182 static int
183 IsNumeric(char *name)
184 {
185     int result, len, i;
186     char *ptr;
187
188     result = 1;
189     ptr = name;
190     len = strlen(name);
191     for (i = 0; i < len; i++) {
192         if (*ptr < '0' || *ptr > '9') {
193             result = 0;
194             break;
195         }
196         ptr++;
197
198     }
199     return result;
200 }
201
202
203 /*
204  * Parse a server dotted address and return the address in network byte order
205  */
206 afs_int32
207 GetServerNoresolve(char *aname)
208 {
209     int b1, b2, b3, b4;
210     afs_int32 addr;
211     afs_int32 code;
212
213     code = sscanf(aname, "%d.%d.%d.%d", &b1, &b2, &b3, &b4);
214     if (code == 4) {
215         addr = (b1 << 24) | (b2 << 16) | (b3 << 8) | b4;
216         addr = htonl(addr);     /* convert to network byte order */
217         return addr;
218     } else
219         return 0;
220 }
221 /*
222  * Parse a server name/address and return the address in network byte order
223  */
224 afs_int32
225 GetServer(char *aname)
226 {
227     register struct hostent *th;
228     afs_int32 addr; /* in network byte order */
229     register afs_int32 code;
230     char hostname[MAXHOSTCHARS];
231
232     if ((addr = GetServerNoresolve(aname)) == 0) {
233         th = gethostbyname(aname);
234         if (!th)
235             return 0;
236         memcpy(&addr, th->h_addr, sizeof(addr));
237     }
238
239     if (addr == htonl(0x7f000001)) {    /* local host */
240         code = gethostname(hostname, MAXHOSTCHARS);
241         if (code)
242             return 0;
243         th = gethostbyname(hostname);
244         if (!th)
245             return 0;
246         memcpy(&addr, th->h_addr, sizeof(addr));
247     }
248
249     return (addr);
250 }
251
252 afs_int32
253 GetVolumeType(char *aname)
254 {
255
256     if (!strcmp(aname, "ro"))
257         return (ROVOL);
258     else if (!strcmp(aname, "rw"))
259         return (RWVOL);
260     else if (!strcmp(aname, "bk"))
261         return (BACKVOL);
262     else
263         return (-1);
264 }
265
266 int
267 IsPartValid(afs_int32 partId, afs_int32 server, afs_int32 *code)
268 {
269     struct partList dummyPartList;
270     int i, success, cnt;
271
272     success = 0;
273     *code = 0;
274
275     *code = UV_ListPartitions(server, &dummyPartList, &cnt);
276     if (*code)
277         return success;
278     for (i = 0; i < cnt; i++) {
279         if (dummyPartList.partFlags[i] & PARTVALID)
280             if (dummyPartList.partId[i] == partId)
281                 success = 1;
282     }
283     return success;
284 }
285
286
287
288  /*sends the contents of file associated with <fd> and <blksize>  to Rx Stream 
289   * associated  with <call> */
290 int 
291 SendFile(usd_handle_t ufd, register struct rx_call *call, long blksize)
292 {
293     char *buffer = (char *)0;
294     afs_int32 error = 0;
295     int done = 0;
296     afs_uint32 nbytes;
297
298     buffer = (char *)malloc(blksize);
299     if (!buffer) {
300         fprintf(STDERR, "malloc failed\n");
301         return -1;
302     }
303
304     while (!error && !done) {
305 #ifndef AFS_NT40_ENV            /* NT csn't select on non-socket fd's */
306         fd_set in;
307         FD_ZERO(&in);
308         FD_SET((intptr_t)(ufd->handle), &in);
309         /* don't timeout if read blocks */
310 #if defined(AFS_PTHREAD_ENV)
311         select(((intptr_t)(ufd->handle)) + 1, &in, 0, 0, 0);
312 #else
313         IOMGR_Select(((intptr_t)(ufd->handle)) + 1, &in, 0, 0, 0);
314 #endif
315 #endif
316         error = USD_READ(ufd, buffer, blksize, &nbytes);
317         if (error) {
318             fprintf(STDERR, "File system read failed: %s\n",
319                     afs_error_message(error));
320             break;
321         }
322         if (nbytes == 0) {
323             done = 1;
324             break;
325         }
326         if (rx_Write(call, buffer, nbytes) != nbytes) {
327             error = -1;
328             break;
329         }
330     }
331     if (buffer)
332         free(buffer);
333     return error;
334 }
335
336 /* function invoked by UV_RestoreVolume, reads the data from rx_trx_stream and
337  * writes it out to the volume. */
338 afs_int32
339 WriteData(struct rx_call *call, void *rock)
340 {
341     char *filename = (char *) rock;
342     usd_handle_t ufd;
343     long blksize;
344     afs_int32 error, code;
345     int ufdIsOpen = 0;
346     afs_hyper_t filesize, currOffset; 
347     afs_uint32 buffer;          
348     afs_uint32 got;             
349
350     error = 0;
351
352     if (!filename || !*filename) {
353         usd_StandardInput(&ufd);
354         blksize = 4096;
355         ufdIsOpen = 1;
356     } else {
357         code = usd_Open(filename, USD_OPEN_RDONLY, 0, &ufd);
358         if (code == 0) {
359             ufdIsOpen = 1;
360             code = USD_IOCTL(ufd, USD_IOCTL_GETBLKSIZE, &blksize);
361         }
362         if (code) {
363             fprintf(STDERR, "Could not access file '%s': %s\n", filename,
364                     afs_error_message(code));
365             error = VOLSERBADOP;
366             goto wfail;
367         }
368         /* test if we have a valid dump */
369         hset64(filesize, 0, 0);
370         USD_SEEK(ufd, filesize, SEEK_END, &currOffset);
371         hset64(filesize, hgethi(currOffset), hgetlo(currOffset)-sizeof(afs_uint32));
372         USD_SEEK(ufd, filesize, SEEK_SET, &currOffset);
373         USD_READ(ufd, (char *)&buffer, sizeof(afs_uint32), &got);
374         if ((got != sizeof(afs_uint32)) || (ntohl(buffer) != DUMPENDMAGIC)) {
375             fprintf(STDERR, "Signature missing from end of file '%s'\n", filename);
376             error = VOLSERBADOP;
377             goto wfail;
378         }
379         /* rewind, we are done */
380         hset64(filesize, 0, 0);
381         USD_SEEK(ufd, filesize, SEEK_SET, &currOffset);
382     }
383     code = SendFile(ufd, call, blksize);
384     if (code) {
385         error = code;
386         goto wfail;
387     }
388   wfail:
389     if (ufdIsOpen) {
390         code = USD_CLOSE(ufd);
391         if (code) {
392             fprintf(STDERR, "Could not close dump file %s\n",
393                     (filename && *filename) ? filename : "STDOUT");
394             if (!error)
395                 error = code;
396         }
397     }
398     return error;
399 }
400
401 /* Receive data from <call> stream into file associated
402  * with <fd> <blksize>
403  */
404 int
405 ReceiveFile(usd_handle_t ufd, struct rx_call *call, long blksize)
406 {
407     char *buffer = NULL;
408     afs_int32 bytesread;
409     afs_uint32 bytesleft, w;
410     afs_int32 error = 0;
411
412     buffer = (char *)malloc(blksize);
413     if (!buffer) {
414         fprintf(STDERR, "memory allocation failed\n");
415         ERROR_EXIT(-1);
416     }
417
418     while ((bytesread = rx_Read(call, buffer, blksize)) > 0) {
419         for (bytesleft = bytesread; bytesleft; bytesleft -= w) {
420 #ifndef AFS_NT40_ENV            /* NT csn't select on non-socket fd's */
421             fd_set out;
422             FD_ZERO(&out);
423             FD_SET((intptr_t)(ufd->handle), &out);
424             /* don't timeout if write blocks */
425 #if defined(AFS_PTHREAD_ENV)
426             select(((intptr_t)(ufd->handle)) + 1, &out, 0, 0, 0);
427 #else
428             IOMGR_Select(((intptr_t)(ufd->handle)) + 1, 0, &out, 0, 0);
429 #endif
430 #endif
431             error =
432                 USD_WRITE(ufd, &buffer[bytesread - bytesleft], bytesleft, &w);
433             if (error) {
434                 fprintf(STDERR, "File system write failed: %s\n",
435                         afs_error_message(error));
436                 ERROR_EXIT(-1);
437             }
438         }
439     }
440
441   error_exit:
442     if (buffer)
443         free(buffer);
444     return (error);
445 }
446
447 afs_int32
448 DumpFunction(struct rx_call *call, void *rock)
449 {
450     char *filename = (char *)rock;
451     usd_handle_t ufd;           /* default is to stdout */
452     afs_int32 error = 0, code;
453     afs_hyper_t size;
454     long blksize;
455     int ufdIsOpen = 0;
456
457     /* Open the output file */
458     if (!filename || !*filename) {
459         usd_StandardOutput(&ufd);
460         blksize = 4096;
461         ufdIsOpen = 1;
462     } else {
463         code =
464             usd_Open(filename, USD_OPEN_CREATE | USD_OPEN_RDWR, 0666, &ufd);
465         if (code == 0) {
466             ufdIsOpen = 1;
467             hzero(size);
468             code = USD_IOCTL(ufd, USD_IOCTL_SETSIZE, &size);
469         }
470         if (code == 0) {
471             code = USD_IOCTL(ufd, USD_IOCTL_GETBLKSIZE, &blksize);
472         }
473         if (code) {
474             fprintf(STDERR, "Could not create file '%s': %s\n", filename,
475                     afs_error_message(code));
476             ERROR_EXIT(VOLSERBADOP);
477         }
478     }
479
480     code = ReceiveFile(ufd, call, blksize);
481     if (code)
482         ERROR_EXIT(code);
483
484   error_exit:
485     /* Close the output file */
486     if (ufdIsOpen) {
487         code = USD_CLOSE(ufd);
488         if (code) {
489             fprintf(STDERR, "Could not close dump file %s\n",
490                     (filename && *filename) ? filename : "STDIN");
491             if (!error)
492                 error = code;
493         }
494     }
495
496     return (error);
497 }
498
499 static void
500 DisplayFormat(volintInfo *pntr, afs_int32 server, afs_int32 part,
501               int *totalOK, int *totalNotOK, int *totalBusy, int fast,
502               int longlist, int disp)
503 {
504     char pname[10];
505     time_t t;
506
507     if (fast) {
508         fprintf(STDOUT, "%-10lu\n", (unsigned long)pntr->volid);
509     } else if (longlist) {
510         if (pntr->status == VOK) {
511             fprintf(STDOUT, "%-32s ", pntr->name);
512             fprintf(STDOUT, "%10lu ", (unsigned long)pntr->volid);
513             if (pntr->type == 0)
514                 fprintf(STDOUT, "RW ");
515             if (pntr->type == 1)
516                 fprintf(STDOUT, "RO ");
517             if (pntr->type == 2)
518                 fprintf(STDOUT, "BK ");
519             fprintf(STDOUT, "%10d K  ", pntr->size);
520             if (pntr->inUse == 1) {
521                 fprintf(STDOUT, "On-line");
522                 *totalOK += 1;
523             } else {
524                 fprintf(STDOUT, "Off-line");
525                 *totalNotOK += 1;
526             }
527             if (pntr->needsSalvaged == 1)
528                 fprintf(STDOUT, "**needs salvage**");
529             fprintf(STDOUT, "\n");
530             MapPartIdIntoName(part, pname);
531             fprintf(STDOUT, "    %s %s \n", hostutil_GetNameByINet(server),
532                     pname);
533             fprintf(STDOUT, "    RWrite %10lu ROnly %10lu Backup %10lu \n",
534                     (unsigned long)pntr->parentID,
535                     (unsigned long)pntr->cloneID,
536                     (unsigned long)pntr->backupID);
537             fprintf(STDOUT, "    MaxQuota %10d K \n", pntr->maxquota);
538             t = pntr->creationDate;
539             fprintf(STDOUT, "    Creation    %s",
540                     ctime(&t));
541             t = pntr->copyDate;
542             fprintf(STDOUT, "    Copy        %s",
543                     ctime(&t));
544
545             t = pntr->backupDate;
546             if (!t)
547                 fprintf(STDOUT, "    Backup      Never\n");
548             else
549                 fprintf(STDOUT, "    Backup      %s",
550                         ctime(&t));
551
552             t = pntr->accessDate;
553             if (t)
554                 fprintf(STDOUT, "    Last Access %s",
555                         ctime(&t));
556
557             t = pntr->updateDate;
558             if (!t)
559                 fprintf(STDOUT, "    Last Update Never\n");
560             else
561                 fprintf(STDOUT, "    Last Update %s",
562                         ctime(&t));
563             fprintf(STDOUT,
564                     "    %d accesses in the past day (i.e., vnode references)\n",
565                     pntr->dayUse);
566         } else if (pntr->status == VBUSY) {
567             *totalBusy += 1;
568             qPut(&busyHead, pntr->volid);
569             if (disp)
570                 fprintf(STDOUT, "**** Volume %lu is busy ****\n",
571                         (unsigned long)pntr->volid);
572         } else {
573             *totalNotOK += 1;
574             qPut(&notokHead, pntr->volid);
575             if (disp)
576                 fprintf(STDOUT, "**** Could not attach volume %lu ****\n",
577                         (unsigned long)pntr->volid);
578         }
579         fprintf(STDOUT, "\n");
580     } else {                    /* default listing */
581         if (pntr->status == VOK) {
582             fprintf(STDOUT, "%-32s ", pntr->name);
583             fprintf(STDOUT, "%10lu ", (unsigned long)pntr->volid);
584             if (pntr->type == 0)
585                 fprintf(STDOUT, "RW ");
586             if (pntr->type == 1)
587                 fprintf(STDOUT, "RO ");
588             if (pntr->type == 2)
589                 fprintf(STDOUT, "BK ");
590             fprintf(STDOUT, "%10d K ", pntr->size);
591             if (pntr->inUse == 1) {
592                 fprintf(STDOUT, "On-line");
593                 *totalOK += 1;
594             } else {
595                 fprintf(STDOUT, "Off-line");
596                 *totalNotOK += 1;
597             }
598             if (pntr->needsSalvaged == 1)
599                 fprintf(STDOUT, "**needs salvage**");
600             fprintf(STDOUT, "\n");
601         } else if (pntr->status == VBUSY) {
602             *totalBusy += 1;
603             qPut(&busyHead, pntr->volid);
604             if (disp)
605                 fprintf(STDOUT, "**** Volume %lu is busy ****\n",
606                         (unsigned long)pntr->volid);
607         } else {
608             *totalNotOK += 1;
609             qPut(&notokHead, pntr->volid);
610             if (disp)
611                 fprintf(STDOUT, "**** Could not attach volume %lu ****\n",
612                         (unsigned long)pntr->volid);
613         }
614     }
615 }
616
617 /*------------------------------------------------------------------------
618  * PRIVATE XDisplayFormat
619  *
620  * Description:
621  *      Display the contents of one extended volume info structure.
622  *
623  * Arguments:
624  *      a_xInfoP        : Ptr to extended volume info struct to print.
625  *      a_servID        : Server ID to print.
626  *      a_partID        : Partition ID to print.
627  *      a_totalOKP      : Ptr to total-OK counter.
628  *      a_totalNotOKP   : Ptr to total-screwed counter.
629  *      a_totalBusyP    : Ptr to total-busy counter.
630  *      a_fast          : Fast listing?
631  *      a_int32         : Int32 listing?
632  *      a_showProblems  : Show volume problems?
633  *
634  * Returns:
635  *      Nothing.
636  *
637  * Environment:
638  *      Nothing interesting.
639  *
640  * Side Effects:
641  *      As advertised.
642  *------------------------------------------------------------------------*/
643
644 static void
645 XDisplayFormat(volintXInfo *a_xInfoP, afs_int32 a_servID, afs_int32 a_partID,
646                int *a_totalOKP, int *a_totalNotOKP, int *a_totalBusyP,
647                int a_fast, int a_int32, int a_showProblems)
648 {                               /*XDisplayFormat */
649     time_t t;
650     char pname[10];
651
652     if (a_fast) {
653         /*
654          * Short & sweet.
655          */
656         fprintf(STDOUT, "%-10lu\n", (unsigned long)a_xInfoP->volid);
657     } else if (a_int32) {
658         /*
659          * Fully-detailed listing.
660          */
661         if (a_xInfoP->status == VOK) {
662             /*
663              * Volume's status is OK - all the fields are valid.
664              */
665             fprintf(STDOUT, "%-32s ", a_xInfoP->name);
666             fprintf(STDOUT, "%10lu ", (unsigned long)a_xInfoP->volid);
667             if (a_xInfoP->type == 0)
668                 fprintf(STDOUT, "RW ");
669             if (a_xInfoP->type == 1)
670                 fprintf(STDOUT, "RO ");
671             if (a_xInfoP->type == 2)
672                 fprintf(STDOUT, "BK ");
673             fprintf(STDOUT, "%10d K used ", a_xInfoP->size);
674             fprintf(STDOUT, "%d files ", a_xInfoP->filecount);
675             if (a_xInfoP->inUse == 1) {
676                 fprintf(STDOUT, "On-line");
677                 (*a_totalOKP)++;
678             } else {
679                 fprintf(STDOUT, "Off-line");
680                 (*a_totalNotOKP)++;
681             }
682             fprintf(STDOUT, "\n");
683             MapPartIdIntoName(a_partID, pname);
684             fprintf(STDOUT, "    %s %s \n", hostutil_GetNameByINet(a_servID),
685                     pname);
686             fprintf(STDOUT, "    RWrite %10lu ROnly %10lu Backup %10lu \n",
687                     (unsigned long)a_xInfoP->parentID,
688                     (unsigned long)a_xInfoP->cloneID,
689                     (unsigned long)a_xInfoP->backupID);
690             fprintf(STDOUT, "    MaxQuota %10d K \n", a_xInfoP->maxquota);
691
692             t = a_xInfoP->creationDate;
693             fprintf(STDOUT, "    Creation    %s",
694                     ctime(&t));
695
696             t = a_xInfoP->copyDate;
697             fprintf(STDOUT, "    Copy        %s",
698                     ctime(&t));
699
700             t = a_xInfoP->backupDate;
701             if (!t)
702                 fprintf(STDOUT, "    Backup      Never\n");
703             else
704                 fprintf(STDOUT, "    Backup      %s",
705                         ctime(&t));
706
707             t = a_xInfoP->accessDate;
708             if (t)
709                 fprintf(STDOUT, "    Last Access %s",
710                         ctime(&t));
711
712             t = a_xInfoP->updateDate;
713             if (!t)
714                 fprintf(STDOUT, "    Last Update Never\n");
715             else
716                 fprintf(STDOUT, "    Last Update %s",
717                         ctime(&t));
718             fprintf(STDOUT,
719                     "    %d accesses in the past day (i.e., vnode references)\n",
720                     a_xInfoP->dayUse);
721
722             /*
723              * Print all the read/write and authorship stats.
724              */
725             fprintf(STDOUT, "\n                      Raw Read/Write Stats\n");
726             fprintf(STDOUT,
727                     "          |-------------------------------------------|\n");
728             fprintf(STDOUT,
729                     "          |    Same Network     |    Diff Network     |\n");
730             fprintf(STDOUT,
731                     "          |----------|----------|----------|----------|\n");
732             fprintf(STDOUT,
733                     "          |  Total   |   Auth   |   Total  |   Auth   |\n");
734             fprintf(STDOUT,
735                     "          |----------|----------|----------|----------|\n");
736             fprintf(STDOUT, "Reads     | %8d | %8d | %8d | %8d |\n",
737                     a_xInfoP->stat_reads[VOLINT_STATS_SAME_NET],
738                     a_xInfoP->stat_reads[VOLINT_STATS_SAME_NET_AUTH],
739                     a_xInfoP->stat_reads[VOLINT_STATS_DIFF_NET],
740                     a_xInfoP->stat_reads[VOLINT_STATS_DIFF_NET_AUTH]);
741             fprintf(STDOUT, "Writes    | %8d | %8d | %8d | %8d |\n",
742                     a_xInfoP->stat_writes[VOLINT_STATS_SAME_NET],
743                     a_xInfoP->stat_writes[VOLINT_STATS_SAME_NET_AUTH],
744                     a_xInfoP->stat_writes[VOLINT_STATS_DIFF_NET],
745                     a_xInfoP->stat_writes[VOLINT_STATS_DIFF_NET_AUTH]);
746             fprintf(STDOUT,
747                     "          |-------------------------------------------|\n\n");
748
749             fprintf(STDOUT,
750                     "                   Writes Affecting Authorship\n");
751             fprintf(STDOUT,
752                     "          |-------------------------------------------|\n");
753             fprintf(STDOUT,
754                     "          |   File Authorship   | Directory Authorship|\n");
755             fprintf(STDOUT,
756                     "          |----------|----------|----------|----------|\n");
757             fprintf(STDOUT,
758                     "          |   Same   |   Diff   |    Same  |   Diff   |\n");
759             fprintf(STDOUT,
760                     "          |----------|----------|----------|----------|\n");
761             fprintf(STDOUT, "0-60 sec  | %8d | %8d | %8d | %8d |\n",
762                     a_xInfoP->stat_fileSameAuthor[VOLINT_STATS_TIME_IDX_0],
763                     a_xInfoP->stat_fileDiffAuthor[VOLINT_STATS_TIME_IDX_0],
764                     a_xInfoP->stat_dirSameAuthor[VOLINT_STATS_TIME_IDX_0],
765                     a_xInfoP->stat_dirDiffAuthor[VOLINT_STATS_TIME_IDX_0]);
766             fprintf(STDOUT, "1-10 min  | %8d | %8d | %8d | %8d |\n",
767                     a_xInfoP->stat_fileSameAuthor[VOLINT_STATS_TIME_IDX_1],
768                     a_xInfoP->stat_fileDiffAuthor[VOLINT_STATS_TIME_IDX_1],
769                     a_xInfoP->stat_dirSameAuthor[VOLINT_STATS_TIME_IDX_1],
770                     a_xInfoP->stat_dirDiffAuthor[VOLINT_STATS_TIME_IDX_1]);
771             fprintf(STDOUT, "10min-1hr | %8d | %8d | %8d | %8d |\n",
772                     a_xInfoP->stat_fileSameAuthor[VOLINT_STATS_TIME_IDX_2],
773                     a_xInfoP->stat_fileDiffAuthor[VOLINT_STATS_TIME_IDX_2],
774                     a_xInfoP->stat_dirSameAuthor[VOLINT_STATS_TIME_IDX_2],
775                     a_xInfoP->stat_dirDiffAuthor[VOLINT_STATS_TIME_IDX_2]);
776             fprintf(STDOUT, "1hr-1day  | %8d | %8d | %8d | %8d |\n",
777                     a_xInfoP->stat_fileSameAuthor[VOLINT_STATS_TIME_IDX_3],
778                     a_xInfoP->stat_fileDiffAuthor[VOLINT_STATS_TIME_IDX_3],
779                     a_xInfoP->stat_dirSameAuthor[VOLINT_STATS_TIME_IDX_3],
780                     a_xInfoP->stat_dirDiffAuthor[VOLINT_STATS_TIME_IDX_3]);
781             fprintf(STDOUT, "1day-1wk  | %8d | %8d | %8d | %8d |\n",
782                     a_xInfoP->stat_fileSameAuthor[VOLINT_STATS_TIME_IDX_4],
783                     a_xInfoP->stat_fileDiffAuthor[VOLINT_STATS_TIME_IDX_4],
784                     a_xInfoP->stat_dirSameAuthor[VOLINT_STATS_TIME_IDX_4],
785                     a_xInfoP->stat_dirDiffAuthor[VOLINT_STATS_TIME_IDX_4]);
786             fprintf(STDOUT, "> 1wk     | %8d | %8d | %8d | %8d |\n",
787                     a_xInfoP->stat_fileSameAuthor[VOLINT_STATS_TIME_IDX_5],
788                     a_xInfoP->stat_fileDiffAuthor[VOLINT_STATS_TIME_IDX_5],
789                     a_xInfoP->stat_dirSameAuthor[VOLINT_STATS_TIME_IDX_5],
790                     a_xInfoP->stat_dirDiffAuthor[VOLINT_STATS_TIME_IDX_5]);
791             fprintf(STDOUT,
792                     "          |-------------------------------------------|\n");
793         } /*Volume status OK */
794         else if (a_xInfoP->status == VBUSY) {
795             (*a_totalBusyP)++;
796             qPut(&busyHead, a_xInfoP->volid);
797             if (a_showProblems)
798                 fprintf(STDOUT, "**** Volume %lu is busy ****\n",
799                         (unsigned long)a_xInfoP->volid);
800         } /*Busy volume */
801         else {
802             (*a_totalNotOKP)++;
803             qPut(&notokHead, a_xInfoP->volid);
804             if (a_showProblems)
805                 fprintf(STDOUT, "**** Could not attach volume %lu ****\n",
806                         (unsigned long)a_xInfoP->volid);
807         }                       /*Screwed volume */
808         fprintf(STDOUT, "\n");
809     } /*Long listing */
810     else {
811         /*
812          * Default listing.
813          */
814         if (a_xInfoP->status == VOK) {
815             fprintf(STDOUT, "%-32s ", a_xInfoP->name);
816             fprintf(STDOUT, "%10lu ", (unsigned long)a_xInfoP->volid);
817             if (a_xInfoP->type == 0)
818                 fprintf(STDOUT, "RW ");
819             if (a_xInfoP->type == 1)
820                 fprintf(STDOUT, "RO ");
821             if (a_xInfoP->type == 2)
822                 fprintf(STDOUT, "BK ");
823             fprintf(STDOUT, "%10d K ", a_xInfoP->size);
824             if (a_xInfoP->inUse == 1) {
825                 fprintf(STDOUT, "On-line");
826                 (*a_totalOKP)++;
827             } else {
828                 fprintf(STDOUT, "Off-line");
829                 (*a_totalNotOKP)++;
830             }
831             fprintf(STDOUT, "\n");
832         } /*Volume OK */
833         else if (a_xInfoP->status == VBUSY) {
834             (*a_totalBusyP)++;
835             qPut(&busyHead, a_xInfoP->volid);
836             if (a_showProblems)
837                 fprintf(STDOUT, "**** Volume %lu is busy ****\n",
838                         (unsigned long)a_xInfoP->volid);
839         } /*Busy volume */
840         else {
841             (*a_totalNotOKP)++;
842             qPut(&notokHead, a_xInfoP->volid);
843             if (a_showProblems)
844                 fprintf(STDOUT, "**** Could not attach volume %lu ****\n",
845                         (unsigned long)a_xInfoP->volid);
846         }                       /*Screwed volume */
847     }                           /*Default listing */
848 }                               /*XDisplayFormat */
849
850 /*------------------------------------------------------------------------
851  * PRIVATE XDisplayFormat2
852  *
853  * Description:
854  *      Display the formated contents of one extended volume info structure.
855  *
856  * Arguments:
857  *      a_xInfoP        : Ptr to extended volume info struct to print.
858  *      a_servID        : Server ID to print.
859  *      a_partID        : Partition ID to print.
860  *      a_totalOKP      : Ptr to total-OK counter.
861  *      a_totalNotOKP   : Ptr to total-screwed counter.
862  *      a_totalBusyP    : Ptr to total-busy counter.
863  *      a_fast          : Fast listing?
864  *      a_int32         : Int32 listing?
865  *      a_showProblems  : Show volume problems?
866  *
867  * Returns:
868  *      Nothing.
869  *
870  * Environment:
871  *      Nothing interesting.
872  *
873  * Side Effects:
874  *      As advertised.
875  *------------------------------------------------------------------------*/
876
877 static void
878 XDisplayFormat2(volintXInfo *a_xInfoP, afs_int32 a_servID, afs_int32 a_partID,
879                 int *a_totalOKP, int *a_totalNotOKP, int *a_totalBusyP,
880                 int a_fast, int a_int32, int a_showProblems)
881 {                               /*XDisplayFormat */
882     time_t t;
883     if (a_fast) {
884         /*
885          * Short & sweet.
886          */
887         fprintf(STDOUT, "vold_id\t%-10lu\n", (unsigned long)a_xInfoP->volid);
888     } else if (a_int32) {
889         /*
890          * Fully-detailed listing.
891          */
892         if (a_xInfoP->status == VOK) {
893             /*
894              * Volume's status is OK - all the fields are valid.
895              */
896
897                 static long server_cache = -1, partition_cache = -1;
898                 static char hostname[256], address[32], pname[16];
899                 int i,ai[] = {VOLINT_STATS_TIME_IDX_0,VOLINT_STATS_TIME_IDX_1,VOLINT_STATS_TIME_IDX_2,
900                               VOLINT_STATS_TIME_IDX_3,VOLINT_STATS_TIME_IDX_4,VOLINT_STATS_TIME_IDX_5};
901
902                 if (a_servID != server_cache) {
903                         struct in_addr s;
904
905                         s.s_addr = a_servID;
906                         strcpy(hostname, hostutil_GetNameByINet(a_servID));
907                         strcpy(address, inet_ntoa(s));
908                         server_cache = a_servID;
909                 }
910                 if (a_partID != partition_cache) {
911                         MapPartIdIntoName(a_partID, pname);
912                         partition_cache = a_partID;
913                 }
914
915                 fprintf(STDOUT, "name\t\t%s\n", a_xInfoP->name);
916                 fprintf(STDOUT, "id\t\t%lu\n", afs_printable_uint32_lu(a_xInfoP->volid));
917                 fprintf(STDOUT, "serv\t\t%s\t%s\n", address, hostname);
918                 fprintf(STDOUT, "part\t\t%s\n", pname);
919                 fprintf(STDOUT, "status\t\tOK\n");
920                 fprintf(STDOUT, "backupID\t%lu\n", 
921                         afs_printable_uint32_lu(a_xInfoP->backupID));
922                 fprintf(STDOUT, "parentID\t%lu\n", 
923                         afs_printable_uint32_lu(a_xInfoP->parentID));
924                 fprintf(STDOUT, "cloneID\t\t%lu\n", 
925                         afs_printable_uint32_lu(a_xInfoP->cloneID));
926                 fprintf(STDOUT, "inUse\t\t%s\n", a_xInfoP->inUse ? "Y" : "N");
927                 switch (a_xInfoP->type) {
928                 case 0:
929                         fprintf(STDOUT, "type\t\tRW\n");
930                         break;
931                 case 1:
932                         fprintf(STDOUT, "type\t\tRO\n");
933                         break;
934                 case 2:
935                         fprintf(STDOUT, "type\t\tBK\n");
936                         break;
937                 default:
938                         fprintf(STDOUT, "type\t\t?\n");
939                         break;
940                 }
941                 t = a_xInfoP->creationDate;
942                 fprintf(STDOUT, "creationDate\t%-9lu\t%s", 
943                         afs_printable_uint32_lu(a_xInfoP->creationDate),
944                         ctime(&t));
945
946                 t = a_xInfoP->accessDate;
947                 fprintf(STDOUT, "accessDate\t%-9lu\t%s", 
948                         afs_printable_uint32_lu(a_xInfoP->accessDate),
949                         ctime(&t));
950
951                 t = a_xInfoP->updateDate;
952                 fprintf(STDOUT, "updateDate\t%-9lu\t%s", 
953                         afs_printable_uint32_lu(a_xInfoP->updateDate),
954                         ctime(&t));
955
956                 t = a_xInfoP->backupDate;
957                 fprintf(STDOUT, "backupDate\t%-9lu\t%s", 
958                         afs_printable_uint32_lu(a_xInfoP->backupDate),
959                         ctime(&t));
960
961                 t = a_xInfoP->copyDate;
962                 fprintf(STDOUT, "copyDate\t%-9lu\t%s", 
963                         afs_printable_uint32_lu(a_xInfoP->copyDate),
964                         ctime(&t));
965                 
966                 fprintf(STDOUT, "diskused\t%u\n", a_xInfoP->size);
967                 fprintf(STDOUT, "maxquota\t%u\n", a_xInfoP->maxquota);
968
969                 fprintf(STDOUT, "filecount\t%u\n", a_xInfoP->filecount);
970                 fprintf(STDOUT, "dayUse\t\t%u\n", a_xInfoP->dayUse);
971
972
973
974                 fprintf(STDOUT,"reads_same_net\t%8d\n",a_xInfoP->stat_reads[VOLINT_STATS_SAME_NET]);
975                 fprintf(STDOUT,"reads_same_net_auth\t%8d\n",a_xInfoP->stat_reads[VOLINT_STATS_SAME_NET_AUTH]);
976                 fprintf(STDOUT,"reads_diff_net\t%8d\n",a_xInfoP->stat_reads[VOLINT_STATS_DIFF_NET]);
977                 fprintf(STDOUT,"reads_diff_net_auth\t%8d\n",a_xInfoP->stat_reads[VOLINT_STATS_DIFF_NET_AUTH]);
978
979                 fprintf(STDOUT,"writes_same_net\t%8d\n",a_xInfoP->stat_writes[VOLINT_STATS_SAME_NET]);
980                 fprintf(STDOUT,"writes_same_net_auth\t%8d\n",a_xInfoP->stat_writes[VOLINT_STATS_SAME_NET_AUTH]);
981                 fprintf(STDOUT,"writes_diff_net\t%8d\n",a_xInfoP->stat_writes[VOLINT_STATS_DIFF_NET]);
982                 fprintf(STDOUT,"writes_diff_net_auth\t%8d\n",a_xInfoP->stat_writes[VOLINT_STATS_DIFF_NET_AUTH]);
983
984                 for(i=0;i<5;i++)
985                 {
986                         fprintf(STDOUT,"file_same_author_idx_%d\t%8d\n",i+1,a_xInfoP->stat_fileSameAuthor[ai[i]]);
987                         fprintf(STDOUT,"file_diff_author_idx_%d\t%8d\n",i+1,a_xInfoP->stat_fileDiffAuthor[ai[i]]);
988                         fprintf(STDOUT,"dir_same_author_idx_%d\t%8d\n",i+1,a_xInfoP->stat_dirSameAuthor[ai[i]]);
989                         fprintf(STDOUT,"dir_dif_author_idx_%d\t%8d\n",i+1,a_xInfoP->stat_dirDiffAuthor[ai[i]]);
990                 }
991
992         } /*Volume status OK */
993         else if (a_xInfoP->status == VBUSY) {
994             (*a_totalBusyP)++;
995             qPut(&busyHead, a_xInfoP->volid);
996             if (a_showProblems)
997                 fprintf(STDOUT, "BUSY_VOL\t%lu\n",
998                         (unsigned long)a_xInfoP->volid);
999         } /*Busy volume */
1000         else {
1001             (*a_totalNotOKP)++;
1002             qPut(&notokHead, a_xInfoP->volid);
1003             if (a_showProblems)
1004                 fprintf(STDOUT, "COULD_NOT_ATTACH\t%lu\n",
1005                         (unsigned long)a_xInfoP->volid);
1006         }                       /*Screwed volume */
1007     } /*Long listing */
1008     else {
1009         /*
1010          * Default listing.
1011          */
1012         if (a_xInfoP->status == VOK) {
1013             fprintf(STDOUT, "name\t%-32s\n", a_xInfoP->name);
1014             fprintf(STDOUT, "volID\t%10lu\n", (unsigned long)a_xInfoP->volid);
1015             if (a_xInfoP->type == 0)
1016                 fprintf(STDOUT, "type\tRW\n");
1017             if (a_xInfoP->type == 1)
1018                 fprintf(STDOUT, "type\tRO\n");
1019             if (a_xInfoP->type == 2)
1020                 fprintf(STDOUT, "type\tBK\n");
1021             fprintf(STDOUT, "size\t%10dK\n", a_xInfoP->size);
1022
1023             fprintf(STDOUT, "inUse\t%d\n",a_xInfoP->inUse);
1024             if (a_xInfoP->inUse == 1)
1025                 (*a_totalOKP)++;
1026             else
1027                 (*a_totalNotOKP)++;
1028
1029         } /*Volume OK */
1030         else if (a_xInfoP->status == VBUSY) {
1031             (*a_totalBusyP)++;
1032             qPut(&busyHead, a_xInfoP->volid);
1033             if (a_showProblems)
1034                 fprintf(STDOUT, "VOLUME_BUSY\t%lu\n",
1035                         (unsigned long)a_xInfoP->volid);
1036         } /*Busy volume */
1037         else {
1038             (*a_totalNotOKP)++;
1039             qPut(&notokHead, a_xInfoP->volid);
1040             if (a_showProblems)
1041                 fprintf(STDOUT, "COULD_NOT_ATTACH_VOLUME\t%lu\n",
1042                         (unsigned long)a_xInfoP->volid);
1043         }                       /*Screwed volume */
1044     }                           /*Default listing */
1045 }                               /*XDisplayFormat */
1046
1047 static void
1048 DisplayFormat2(long server, long partition, volintInfo *pntr)
1049 {
1050     static long server_cache = -1, partition_cache = -1;
1051     static char hostname[256], address[32], pname[16];
1052     time_t t;
1053
1054     if (server != server_cache) {
1055         struct in_addr s;
1056
1057         s.s_addr = server;
1058         strcpy(hostname, hostutil_GetNameByINet(server));
1059         strcpy(address, inet_ntoa(s));
1060         server_cache = server;
1061     }
1062     if (partition != partition_cache) {
1063         MapPartIdIntoName(partition, pname);
1064         partition_cache = partition;
1065     }
1066
1067     if (pntr->status == VOK)
1068         fprintf(STDOUT, "name\t\t%s\n", pntr->name);
1069
1070     fprintf(STDOUT, "id\t\t%lu\n", 
1071             afs_printable_uint32_lu(pntr->volid));
1072     fprintf(STDOUT, "serv\t\t%s\t%s\n", address, hostname);
1073     fprintf(STDOUT, "part\t\t%s\n", pname);
1074     switch (pntr->status) {
1075     case VOK:
1076         fprintf(STDOUT, "status\t\tOK\n");
1077         break;
1078     case VBUSY:
1079         fprintf(STDOUT, "status\t\tBUSY\n");
1080         return;
1081     default:
1082         fprintf(STDOUT, "status\t\tUNATTACHABLE\n");
1083         return;
1084     }
1085     fprintf(STDOUT, "backupID\t%lu\n", 
1086             afs_printable_uint32_lu(pntr->backupID));
1087     fprintf(STDOUT, "parentID\t%lu\n", 
1088             afs_printable_uint32_lu(pntr->parentID));
1089     fprintf(STDOUT, "cloneID\t\t%lu\n", 
1090             afs_printable_uint32_lu(pntr->cloneID));
1091     fprintf(STDOUT, "inUse\t\t%s\n", pntr->inUse ? "Y" : "N");
1092     fprintf(STDOUT, "needsSalvaged\t%s\n", pntr->needsSalvaged ? "Y" : "N");
1093     /* 0xD3 is from afs/volume.h since I had trouble including the file */
1094     fprintf(STDOUT, "destroyMe\t%s\n", pntr->destroyMe == 0xD3 ? "Y" : "N");
1095     switch (pntr->type) {
1096     case 0:
1097         fprintf(STDOUT, "type\t\tRW\n");
1098         break;
1099     case 1:
1100         fprintf(STDOUT, "type\t\tRO\n");
1101         break;
1102     case 2:
1103         fprintf(STDOUT, "type\t\tBK\n");
1104         break;
1105     default:
1106         fprintf(STDOUT, "type\t\t?\n");
1107         break;
1108     }
1109     t = pntr->creationDate;
1110     fprintf(STDOUT, "creationDate\t%-9lu\t%s", 
1111             afs_printable_uint32_lu(pntr->creationDate),
1112             ctime(&t));
1113
1114     t = pntr->accessDate;
1115     fprintf(STDOUT, "accessDate\t%-9lu\t%s", 
1116             afs_printable_uint32_lu(pntr->accessDate),
1117             ctime(&t));
1118
1119     t = pntr->updateDate;
1120     fprintf(STDOUT, "updateDate\t%-9lu\t%s", 
1121             afs_printable_uint32_lu(pntr->updateDate),
1122             ctime(&t));
1123
1124     t = pntr->backupDate;
1125     fprintf(STDOUT, "backupDate\t%-9lu\t%s", 
1126             afs_printable_uint32_lu(pntr->backupDate),
1127             ctime(&t));
1128
1129     t = pntr->copyDate;
1130     fprintf(STDOUT, "copyDate\t%-9lu\t%s", 
1131             afs_printable_uint32_lu(pntr->copyDate),
1132             ctime(&t));
1133
1134     fprintf(STDOUT, "flags\t\t%#lx\t(Optional)\n", 
1135             afs_printable_uint32_lu(pntr->flags));
1136     fprintf(STDOUT, "diskused\t%u\n", pntr->size);
1137     fprintf(STDOUT, "maxquota\t%u\n", pntr->maxquota);
1138     fprintf(STDOUT, "minquota\t%lu\t(Optional)\n", 
1139             afs_printable_uint32_lu(pntr->spare0));
1140     fprintf(STDOUT, "filecount\t%u\n", pntr->filecount);
1141     fprintf(STDOUT, "dayUse\t\t%u\n", pntr->dayUse);
1142     fprintf(STDOUT, "weekUse\t\t%lu\t(Optional)\n",
1143             afs_printable_uint32_lu(pntr->spare1));
1144     fprintf(STDOUT, "spare2\t\t%lu\t(Optional)\n", 
1145             afs_printable_uint32_lu(pntr->spare2));
1146     fprintf(STDOUT, "spare3\t\t%lu\t(Optional)\n", 
1147             afs_printable_uint32_lu(pntr->spare3));
1148     return;
1149 }
1150
1151 static void
1152 DisplayVolumes2(long server, long partition, volintInfo *pntr, long count)
1153 {
1154     long i;
1155
1156     for (i = 0; i < count; i++) {
1157         fprintf(STDOUT, "BEGIN_OF_ENTRY\n");
1158         DisplayFormat2(server, partition, pntr);
1159         fprintf(STDOUT, "END_OF_ENTRY\n\n");
1160         pntr++;
1161     }
1162     return;
1163 }
1164
1165 static void
1166 DisplayVolumes(afs_int32 server, afs_int32 part, volintInfo *pntr,
1167                afs_int32 count, afs_int32 longlist, afs_int32 fast,
1168                int quiet)
1169 {
1170     int totalOK, totalNotOK, totalBusy, i;
1171     afs_uint32 volid = 0;
1172
1173     totalOK = 0;
1174     totalNotOK = 0;
1175     totalBusy = 0;
1176     qInit(&busyHead);
1177     qInit(&notokHead);
1178     for (i = 0; i < count; i++) {
1179         DisplayFormat(pntr, server, part, &totalOK, &totalNotOK, &totalBusy,
1180                       fast, longlist, 0);
1181         pntr++;
1182     }
1183     if (totalBusy) {
1184         while (busyHead.count) {
1185             qGet(&busyHead, &volid);
1186             fprintf(STDOUT, "**** Volume %lu is busy ****\n",
1187                     (unsigned long)volid);
1188         }
1189     }
1190     if (totalNotOK) {
1191         while (notokHead.count) {
1192             qGet(&notokHead, &volid);
1193             fprintf(STDOUT, "**** Could not attach volume %lu ****\n",
1194                     (unsigned long)volid);
1195         }
1196     }
1197     if (!quiet) {
1198         fprintf(STDOUT, "\n");
1199         if (!fast) {
1200             fprintf(STDOUT,
1201                     "Total volumes onLine %d ; Total volumes offLine %d ; Total busy %d\n\n",
1202                     totalOK, totalNotOK, totalBusy);
1203         }
1204     }
1205 }
1206 /*------------------------------------------------------------------------
1207  * PRIVATE XDisplayVolumes
1208  *
1209  * Description:
1210  *      Display extended volume information.
1211  *
1212  * Arguments:
1213  *      a_servID : Pointer to the Rx call we're performing.
1214  *      a_partID : Partition for which we want the extended list.
1215  *      a_xInfoP : Ptr to extended volume info.
1216  *      a_count  : Number of volume records contained above.
1217  *      a_int32   : Int32 listing generated?
1218  *      a_fast   : Fast listing generated?
1219  *      a_quiet  : Quiet listing generated?
1220  *
1221  * Returns:
1222  *      Nothing.
1223  *
1224  * Environment:
1225  *      Nothing interesting.
1226  *
1227  * Side Effects:
1228  *      As advertised.
1229  *------------------------------------------------------------------------*/
1230
1231 static void
1232 XDisplayVolumes(afs_int32 a_servID, afs_int32 a_partID, volintXInfo *a_xInfoP,
1233                 afs_int32 a_count, afs_int32 a_int32, afs_int32 a_fast,
1234                 int a_quiet)
1235 {                               /*XDisplayVolumes */
1236
1237     int totalOK;                /*Total OK volumes */
1238     int totalNotOK;             /*Total screwed volumes */
1239     int totalBusy;              /*Total busy volumes */
1240     int i;                      /*Loop variable */
1241     afs_uint32 volid = 0;       /*Current volume ID */
1242
1243     /*
1244      * Initialize counters and (global!!) queues.
1245      */
1246     totalOK = 0;
1247     totalNotOK = 0;
1248     totalBusy = 0;
1249     qInit(&busyHead);
1250     qInit(&notokHead);
1251
1252     /*
1253      * Display each volume in the list.
1254      */
1255     for (i = 0; i < a_count; i++) {
1256         XDisplayFormat(a_xInfoP, a_servID, a_partID, &totalOK, &totalNotOK,
1257                        &totalBusy, a_fast, a_int32, 0);
1258         a_xInfoP++;
1259     }
1260
1261     /*
1262      * If any volumes were found to be busy or screwed, display them.
1263      */
1264     if (totalBusy) {
1265         while (busyHead.count) {
1266             qGet(&busyHead, &volid);
1267             fprintf(STDOUT, "**** Volume %lu is busy ****\n",
1268                     (unsigned long)volid);
1269         }
1270     }
1271     if (totalNotOK) {
1272         while (notokHead.count) {
1273             qGet(&notokHead, &volid);
1274             fprintf(STDOUT, "**** Could not attach volume %lu ****\n",
1275                     (unsigned long)volid);
1276         }
1277     }
1278
1279     if (!a_quiet) {
1280         fprintf(STDOUT, "\n");
1281         if (!a_fast) {
1282             fprintf(STDOUT,
1283                     "Total volumes: %d on-line, %d off-line, %d  busyd\n\n",
1284                     totalOK, totalNotOK, totalBusy);
1285         }
1286     }
1287
1288 }                               /*XDisplayVolumes */
1289
1290 /*------------------------------------------------------------------------
1291  * PRIVATE XDisplayVolumes2
1292  *
1293  * Description:
1294  *      Display extended formated volume information.
1295  *
1296  * Arguments:
1297  *      a_servID : Pointer to the Rx call we're performing.
1298  *      a_partID : Partition for which we want the extended list.
1299  *      a_xInfoP : Ptr to extended volume info.
1300  *      a_count  : Number of volume records contained above.
1301  *      a_int32   : Int32 listing generated?
1302  *      a_fast   : Fast listing generated?
1303  *      a_quiet  : Quiet listing generated?
1304  *
1305  * Returns:
1306  *      Nothing.
1307  *
1308  * Environment:
1309  *      Nothing interesting.
1310  *
1311  * Side Effects:
1312  *      As advertised.
1313  *------------------------------------------------------------------------*/
1314
1315 static void
1316 XDisplayVolumes2(afs_int32 a_servID, afs_int32 a_partID, volintXInfo *a_xInfoP,
1317                  afs_int32 a_count, afs_int32 a_int32, afs_int32 a_fast,
1318                  int a_quiet)
1319 {                               /*XDisplayVolumes */
1320
1321     int totalOK;                /*Total OK volumes */
1322     int totalNotOK;             /*Total screwed volumes */
1323     int totalBusy;              /*Total busy volumes */
1324     int i;                      /*Loop variable */
1325     afs_uint32 volid = 0;       /*Current volume ID */
1326
1327     /*
1328      * Initialize counters and (global!!) queues.
1329      */
1330     totalOK = 0;
1331     totalNotOK = 0;
1332     totalBusy = 0;
1333     qInit(&busyHead);
1334     qInit(&notokHead);
1335
1336     /*
1337      * Display each volume in the list.
1338      */
1339     for (i = 0; i < a_count; i++) {
1340         fprintf(STDOUT, "BEGIN_OF_ENTRY\n");
1341         XDisplayFormat2(a_xInfoP, a_servID, a_partID, &totalOK, &totalNotOK,
1342                        &totalBusy, a_fast, a_int32, 0);
1343         fprintf(STDOUT, "END_OF_ENTRY\n");
1344         a_xInfoP++;
1345     }
1346
1347     /*
1348      * If any volumes were found to be busy or screwed, display them.
1349      */
1350     if (totalBusy) {
1351         while (busyHead.count) {
1352             qGet(&busyHead, &volid);
1353             fprintf(STDOUT, "BUSY_VOL\t%lu\n",
1354                     (unsigned long)volid);
1355         }
1356     }
1357     if (totalNotOK) {
1358         while (notokHead.count) {
1359             qGet(&notokHead, &volid);
1360             fprintf(STDOUT, "COULD_NOT_ATTACH\t%lu\n",
1361                     (unsigned long)volid);
1362         }
1363     }
1364
1365     if (!a_quiet) {
1366         fprintf(STDOUT, "\n");
1367         if (!a_fast) {
1368             fprintf(STDOUT,
1369                     "VOLUMES_ONLINE\t%d\nVOLUMES_OFFLINE\t%d\nVOLUMES_BUSY\t%d\n",
1370                     totalOK, totalNotOK, totalBusy);
1371         }
1372     }
1373
1374 }                               /*XDisplayVolumes2 */
1375
1376
1377 /* set <server> and <part> to the correct values depending on 
1378  * <voltype> and <entry> */
1379 static void
1380 GetServerAndPart(struct nvldbentry *entry, int voltype, afs_int32 *server,
1381                  afs_int32 *part, int *previdx)
1382 {
1383     int i, istart, vtype;
1384
1385     *server = -1;
1386     *part = -1;
1387
1388     /* Doesn't check for non-existance of backup volume */
1389     if ((voltype == RWVOL) || (voltype == BACKVOL)) {
1390         vtype = ITSRWVOL;
1391         istart = 0;             /* seach the entire entry */
1392     } else {
1393         vtype = ITSROVOL;
1394         /* Seach from beginning of entry or pick up where we left off */
1395         istart = ((*previdx < 0) ? 0 : *previdx + 1);
1396     }
1397
1398     for (i = istart; i < entry->nServers; i++) {
1399         if (entry->serverFlags[i] & vtype) {
1400             *server = entry->serverNumber[i];
1401             *part = entry->serverPartition[i];
1402             *previdx = i;
1403             return;
1404         }
1405     }
1406
1407     /* Didn't find any, return -1 */
1408     *previdx = -1;
1409     return;
1410 }
1411
1412 static void
1413 PrintLocked(afs_int32 aflags)
1414 {
1415     afs_int32 flags = aflags & VLOP_ALLOPERS;
1416
1417     if (flags) {
1418         fprintf(STDOUT, "    Volume is currently LOCKED  \n");
1419
1420         if (flags & VLOP_MOVE) {
1421             fprintf(STDOUT, "    Volume is locked for a move operation\n");
1422         }
1423         if (flags & VLOP_RELEASE) {
1424             fprintf(STDOUT, "    Volume is locked for a release operation\n");
1425         }
1426         if (flags & VLOP_BACKUP) {
1427             fprintf(STDOUT, "    Volume is locked for a backup operation\n");
1428         }
1429         if (flags & VLOP_DELETE) {
1430             fprintf(STDOUT, "    Volume is locked for a delete/misc operation\n");
1431         }
1432         if (flags & VLOP_DUMP) {
1433             fprintf(STDOUT, "    Volume is locked for a dump/restore operation\n");
1434         }
1435     }
1436 }
1437
1438 static void
1439 PostVolumeStats(struct nvldbentry *entry)
1440 {
1441     SubEnumerateEntry(entry);
1442     /* Check for VLOP_ALLOPERS */
1443     PrintLocked(entry->flags);
1444     return;
1445 }
1446
1447 /*------------------------------------------------------------------------
1448  * PRIVATE XVolumeStats
1449  *
1450  * Description:
1451  *      Display extended volume information.
1452  *
1453  * Arguments:
1454  *      a_xInfoP  : Ptr to extended volume info.
1455  *      a_entryP  : Ptr to the volume's VLDB entry.
1456  *      a_srvID   : Server ID.
1457  *      a_partID  : Partition ID.
1458  *      a_volType : Type of volume to print.
1459  *
1460  * Returns:
1461  *      Nothing.
1462  *
1463  * Environment:
1464  *      Nothing interesting.
1465  *
1466  * Side Effects:
1467  *      As advertised.
1468  *------------------------------------------------------------------------*/
1469
1470 static void
1471 XVolumeStats(volintXInfo *a_xInfoP, struct nvldbentry *a_entryP,
1472              afs_int32 a_srvID, afs_int32 a_partID, int a_volType)
1473 {                               /*XVolumeStats */
1474
1475     int totalOK, totalNotOK, totalBusy; /*Dummies - we don't really count here */
1476
1477     XDisplayFormat(a_xInfoP,    /*Ptr to extended volume info */
1478                    a_srvID,     /*Server ID to print */
1479                    a_partID,    /*Partition ID to print */
1480                    &totalOK,    /*Ptr to total-OK counter */
1481                    &totalNotOK, /*Ptr to total-screwed counter */
1482                    &totalBusy,  /*Ptr to total-busy counter */
1483                    0,           /*Don't do a fast listing */
1484                    1,           /*Do a long listing */
1485                    1);          /*Show volume problems */
1486     return;
1487
1488 }                               /*XVolumeStats */
1489
1490 static void
1491 VolumeStats_int(volintInfo *pntr, struct nvldbentry *entry, afs_int32 server, 
1492              afs_int32 part, int voltype)
1493 {
1494     int totalOK, totalNotOK, totalBusy;
1495
1496     DisplayFormat(pntr, server, part, &totalOK, &totalNotOK, &totalBusy, 0, 1,
1497                   1);
1498     return;
1499 }
1500
1501 /* command to forcibly remove a volume */
1502 static int
1503 NukeVolume(register struct cmd_syndesc *as)
1504 {
1505     register afs_int32 code;
1506     afs_uint32 volID;
1507     afs_int32  err;
1508     afs_int32 partID;
1509     afs_int32 server;
1510     register char *tp;
1511
1512     server = GetServer(tp = as->parms[0].items->data);
1513     if (!server) {
1514         fprintf(STDERR, "vos: server '%s' not found in host table\n", tp);
1515         return 1;
1516     }
1517
1518     partID = volutil_GetPartitionID(tp = as->parms[1].items->data);
1519     if (partID == -1) {
1520         fprintf(STDERR, "vos: could not parse '%s' as a partition name", tp);
1521         return 1;
1522     }
1523
1524     volID = vsu_GetVolumeID(tp = as->parms[2].items->data, cstruct, &err);
1525     if (volID == 0) {
1526         if (err)
1527             PrintError("", err);
1528         else
1529             fprintf(STDERR,
1530                     "vos: could not parse '%s' as a numeric volume ID", tp);
1531         return 1;
1532     }
1533
1534     fprintf(STDOUT,
1535             "vos: forcibly removing all traces of volume %d, please wait...",
1536             volID);
1537     fflush(STDOUT);
1538     code = UV_NukeVolume(server, partID, volID);
1539     if (code == 0)
1540         fprintf(STDOUT, "done.\n");
1541     else
1542         fprintf(STDOUT, "failed with code %d.\n", code);
1543     return code;
1544 }
1545
1546
1547 /*------------------------------------------------------------------------
1548  * PRIVATE ExamineVolume
1549  *
1550  * Description:
1551  *      Routine used to examine a single volume, contacting the VLDB as
1552  *      well as the Volume Server.
1553  *
1554  * Arguments:
1555  *      as : Ptr to parsed command line arguments.
1556  *
1557  * Returns:
1558  *      0 for a successful operation,
1559  *      Otherwise, one of the ubik or VolServer error values.
1560  *
1561  * Environment:
1562  *      Nothing interesting.
1563  *
1564  * Side Effects:
1565  *      As advertised.
1566  *------------------------------------------------------------------------
1567  */
1568 static int
1569 ExamineVolume(register struct cmd_syndesc *as, void *arock)
1570 {
1571     struct nvldbentry entry;
1572     afs_int32 vcode = 0;
1573     volintInfo *pntr = (volintInfo *) 0;
1574     volintXInfo *xInfoP = (volintXInfo *) 0;
1575     afs_uint32 volid;
1576     afs_int32 code, err, error = 0;
1577     int voltype, foundserv = 0, foundentry = 0;
1578     afs_int32 aserver, apart;
1579     int previdx = -1;
1580     int wantExtendedInfo;       /*Do we want extended vol info? */
1581
1582     wantExtendedInfo = (as->parms[1].items ? 1 : 0);    /* -extended */
1583
1584     volid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err);   /* -id */
1585     if (volid == 0) {
1586         if (err)
1587             PrintError("", err);
1588         else
1589             fprintf(STDERR, "Unknown volume ID or name '%s'\n",
1590                     as->parms[0].items->data);
1591         return -1;
1592     }
1593
1594     if (verbose) {
1595         fprintf(STDOUT, "Fetching VLDB entry for %lu .. ",
1596                 (unsigned long)volid);
1597         fflush(STDOUT);
1598     }
1599     vcode = VLDB_GetEntryByID(volid, -1, &entry);
1600     if (vcode) {
1601         fprintf(STDERR,
1602                 "Could not fetch the entry for volume number %lu from VLDB \n",
1603                 (unsigned long)volid);
1604         return (vcode);
1605     }
1606     if (verbose)
1607         fprintf(STDOUT, "done\n");
1608     MapHostToNetwork(&entry);
1609
1610     if (entry.volumeId[RWVOL] == volid)
1611         voltype = RWVOL;
1612     else if (entry.volumeId[BACKVOL] == volid)
1613         voltype = BACKVOL;
1614     else                        /* (entry.volumeId[ROVOL] == volid) */
1615         voltype = ROVOL;
1616
1617     do {                        /* do {...} while (voltype == ROVOL) */
1618         /* Get the entry for the volume. If its a RW vol, get the RW entry.
1619          * It its a BK vol, get the RW entry (even if VLDB may say the BK doen't exist).
1620          * If its a RO vol, get the next RO entry.
1621          */
1622         GetServerAndPart(&entry, ((voltype == ROVOL) ? ROVOL : RWVOL),
1623                          &aserver, &apart, &previdx);
1624         if (previdx == -1) {    /* searched all entries */
1625             if (!foundentry) {
1626                 fprintf(STDERR, "Volume %s does not exist in VLDB\n\n",
1627                         as->parms[0].items->data);
1628                 error = ENOENT;
1629             }
1630             break;
1631         }
1632         foundentry = 1;
1633
1634         /* Get information about the volume from the server */
1635         if (verbose) {
1636             fprintf(STDOUT, "Getting volume listing from the server %s .. ",
1637                     hostutil_GetNameByINet(aserver));
1638             fflush(STDOUT);
1639         }
1640         if (wantExtendedInfo)
1641             code = UV_XListOneVolume(aserver, apart, volid, &xInfoP);
1642         else
1643             code = UV_ListOneVolume(aserver, apart, volid, &pntr);
1644         if (verbose)
1645             fprintf(STDOUT, "done\n");
1646
1647         if (code) {
1648             error = code;
1649             if (code == ENODEV) {
1650                 if ((voltype == BACKVOL) && !(entry.flags & BACK_EXISTS)) {
1651                     /* The VLDB says there is no backup volume and its not on disk */
1652                     fprintf(STDERR, "Volume %s does not exist\n",
1653                             as->parms[0].items->data);
1654                     error = ENOENT;
1655                 } else {
1656                     fprintf(STDERR,
1657                             "Volume does not exist on server %s as indicated by the VLDB\n",
1658                             hostutil_GetNameByINet(aserver));
1659                 }
1660             } else {
1661                 PrintDiagnostics("examine", code);
1662             }
1663             fprintf(STDOUT, "\n");
1664         } else {
1665             foundserv = 1;
1666             if (wantExtendedInfo)
1667                 XVolumeStats(xInfoP, &entry, aserver, apart, voltype);
1668             else if (as->parms[2].items) {
1669                 DisplayFormat2(aserver, apart, pntr);
1670                 EnumerateEntry(&entry);
1671             } else
1672                 VolumeStats_int(pntr, &entry, aserver, apart, voltype);
1673
1674             if ((voltype == BACKVOL) && !(entry.flags & BACK_EXISTS)) {
1675                 /* The VLDB says there is no backup volume yet we found one on disk */
1676                 fprintf(STDERR, "Volume %s does not exist in VLDB\n",
1677                         as->parms[0].items->data);
1678                 error = ENOENT;
1679             }
1680         }
1681
1682         if (pntr)
1683             free(pntr);
1684         if (xInfoP)
1685             free(xInfoP);
1686     } while (voltype == ROVOL);
1687
1688     if (!foundserv) {
1689         fprintf(STDERR, "Dump only information from VLDB\n\n");
1690         fprintf(STDOUT, "%s \n", entry.name);   /* PostVolumeStats doesn't print name */
1691     }
1692     PostVolumeStats(&entry);
1693
1694     return (error);
1695 }
1696
1697 /*------------------------------------------------------------------------
1698  * PRIVATE SetFields
1699  *
1700  * Description:
1701  *      Routine used to change the status of a single volume.
1702  *
1703  * Arguments:
1704  *      as : Ptr to parsed command line arguments.
1705  *
1706  * Returns:
1707  *      0 for a successful operation,
1708  *      Otherwise, one of the ubik or VolServer error values.
1709  *
1710  * Environment:
1711  *      Nothing interesting.
1712  *
1713  * Side Effects:
1714  *      As advertised.
1715  *------------------------------------------------------------------------
1716  */
1717 static int
1718 SetFields(register struct cmd_syndesc *as, void *arock)
1719 {
1720     struct nvldbentry entry;
1721     volintInfo info;
1722     afs_uint32 volid;
1723     afs_int32 code, err;
1724     afs_int32 aserver, apart;
1725     int previdx = -1;
1726
1727     volid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err);   /* -id */
1728     if (volid == 0) {
1729         if (err)
1730             PrintError("", err);
1731         else
1732             fprintf(STDERR, "Unknown volume ID or name '%s'\n",
1733                     as->parms[0].items->data);
1734         return -1;
1735     }
1736
1737     code = VLDB_GetEntryByID(volid, RWVOL, &entry);
1738     if (code) {
1739         fprintf(STDERR,
1740                 "Could not fetch the entry for volume number %lu from VLDB \n",
1741                 (unsigned long)volid);
1742         return (code);
1743     }
1744     MapHostToNetwork(&entry);
1745
1746     GetServerAndPart(&entry, RWVOL, &aserver, &apart, &previdx);
1747     if (previdx == -1) {
1748         fprintf(STDERR, "Volume %s does not exist in VLDB\n\n",
1749                 as->parms[0].items->data);
1750         return (ENOENT);
1751     }
1752
1753     init_volintInfo(&info);
1754     info.volid = volid;
1755     info.type = RWVOL;
1756
1757     if (as->parms[1].items) {
1758         /* -max <quota> */
1759         code = util_GetHumanInt32(as->parms[1].items->data, &info.maxquota);
1760         if (code) {
1761             fprintf(STDERR, "invalid quota value\n");
1762             return code;
1763         }
1764     }
1765     if (as->parms[2].items) {
1766         /* -clearuse */
1767         info.dayUse = 0;
1768     }
1769     if (as->parms[3].items) {
1770         /* -clearVolUpCounter */
1771         info.spare2 = 0;
1772     }
1773     code = UV_SetVolumeInfo(aserver, apart, volid, &info);
1774     if (code)
1775         fprintf(STDERR,
1776                 "Could not update volume info fields for volume number %lu\n",
1777                 (unsigned long)volid);
1778     return (code);
1779 }
1780
1781 /*------------------------------------------------------------------------
1782  * PRIVATE volOnline
1783  *
1784  * Description:
1785  *      Brings a volume online.
1786  *
1787  * Arguments:
1788  *      as : Ptr to parsed command line arguments.
1789  *
1790  * Returns:
1791  *      0 for a successful operation,
1792  *
1793  * Environment:
1794  *      Nothing interesting.
1795  *
1796  * Side Effects:
1797  *      As advertised.
1798  *------------------------------------------------------------------------
1799  */
1800 static int
1801 volOnline(register struct cmd_syndesc *as, void *arock)
1802 {
1803     afs_int32 server, partition;
1804     afs_uint32 volid;
1805     afs_int32 code, err = 0;
1806
1807     server = GetServer(as->parms[0].items->data);
1808     if (server == 0) {
1809         fprintf(STDERR, "vos: server '%s' not found in host table\n",
1810                 as->parms[0].items->data);
1811         return -1;
1812     }
1813
1814     partition = volutil_GetPartitionID(as->parms[1].items->data);
1815     if (partition < 0) {
1816         fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
1817                 as->parms[1].items->data);
1818         return ENOENT;
1819     }
1820
1821     volid = vsu_GetVolumeID(as->parms[2].items->data, cstruct, &err);   /* -id */
1822     if (!volid) {
1823         if (err)
1824             PrintError("", err);
1825         else
1826             fprintf(STDERR, "Unknown volume ID or name '%s'\n",
1827                     as->parms[0].items->data);
1828         return -1;
1829     }
1830
1831     code = UV_SetVolume(server, partition, volid, ITOffline, 0 /*online */ ,
1832                         0 /*sleep */ );
1833     if (code) {
1834         fprintf(STDERR, "Failed to set volume. Code = %d\n", code);
1835         return -1;
1836     }
1837
1838     return 0;
1839 }
1840
1841 /*------------------------------------------------------------------------
1842  * PRIVATE volOffline
1843  *
1844  * Description:
1845  *      Brings a volume offline.
1846  *
1847  * Arguments:
1848  *      as : Ptr to parsed command line arguments.
1849  *
1850  * Returns:
1851  *      0 for a successful operation,
1852  *
1853  * Environment:
1854  *      Nothing interesting.
1855  *
1856  * Side Effects:
1857  *      As advertised.
1858  *------------------------------------------------------------------------
1859  */
1860 static int
1861 volOffline(register struct cmd_syndesc *as, void *arock)
1862 {
1863     afs_int32 server, partition;
1864     afs_uint32 volid;
1865     afs_int32 code, err = 0;
1866     afs_int32 transflag, sleeptime, transdone;
1867
1868     server = GetServer(as->parms[0].items->data);
1869     if (server == 0) {
1870         fprintf(STDERR, "vos: server '%s' not found in host table\n",
1871                 as->parms[0].items->data);
1872         return -1;
1873     }
1874
1875     partition = volutil_GetPartitionID(as->parms[1].items->data);
1876     if (partition < 0) {
1877         fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
1878                 as->parms[1].items->data);
1879         return ENOENT;
1880     }
1881
1882     volid = vsu_GetVolumeID(as->parms[2].items->data, cstruct, &err);   /* -id */
1883     if (!volid) {
1884         if (err)
1885             PrintError("", err);
1886         else
1887             fprintf(STDERR, "Unknown volume ID or name '%s'\n",
1888                     as->parms[0].items->data);
1889         return -1;
1890     }
1891
1892     transflag = (as->parms[4].items ? ITBusy : ITOffline);
1893     sleeptime = (as->parms[3].items ? atol(as->parms[3].items->data) : 0);
1894     transdone = (sleeptime ? 0 /*online */ : VTOutOfService);
1895     if (as->parms[4].items && !as->parms[3].items) {
1896         fprintf(STDERR, "-sleep option must be used with -busy flag\n");
1897         return -1;
1898     }
1899
1900     code =
1901         UV_SetVolume(server, partition, volid, transflag, transdone,
1902                      sleeptime);
1903     if (code) {
1904         fprintf(STDERR, "Failed to set volume. Code = %d\n", code);
1905         return -1;
1906     }
1907
1908     return 0;
1909 }
1910
1911 static int
1912 CreateVolume(register struct cmd_syndesc *as, void *arock)
1913 {
1914     afs_int32 pnum;
1915     char part[10];
1916     afs_uint32 volid = 0, rovolid = 0, bkvolid = 0;
1917     afs_uint32 *arovolid;
1918     afs_int32 code;
1919     struct nvldbentry entry;
1920     afs_int32 vcode;
1921     afs_int32 quota;
1922
1923     arovolid = &rovolid;
1924
1925     quota = 5000;
1926     tserver = GetServer(as->parms[0].items->data);
1927     if (!tserver) {
1928         fprintf(STDERR, "vos: host '%s' not found in host table\n",
1929                 as->parms[0].items->data);
1930         return ENOENT;
1931     }
1932     pnum = volutil_GetPartitionID(as->parms[1].items->data);
1933     if (pnum < 0) {
1934         fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
1935                 as->parms[1].items->data);
1936         return ENOENT;
1937     }
1938     if (!IsPartValid(pnum, tserver, &code)) {   /*check for validity of the partition */
1939         if (code)
1940             PrintError("", code);
1941         else
1942             fprintf(STDERR,
1943                     "vos : partition %s does not exist on the server\n",
1944                     as->parms[1].items->data);
1945         return ENOENT;
1946     }
1947     if (!ISNAMEVALID(as->parms[2].items->data)) {
1948         fprintf(STDERR,
1949                 "vos: the name of the root volume %s exceeds the size limit of %d\n",
1950                 as->parms[2].items->data, VOLSER_OLDMAXVOLNAME - 10);
1951         return E2BIG;
1952     }
1953     if (!VolNameOK(as->parms[2].items->data)) {
1954         fprintf(STDERR,
1955                 "Illegal volume name %s, should not end in .readonly or .backup\n",
1956                 as->parms[2].items->data);
1957         return EINVAL;
1958     }
1959     if (IsNumeric(as->parms[2].items->data)) {
1960         fprintf(STDERR, "Illegal volume name %s, should not be a number\n",
1961                 as->parms[2].items->data);
1962         return EINVAL;
1963     }
1964     vcode = VLDB_GetEntryByName(as->parms[2].items->data, &entry);
1965     if (!vcode) {
1966         fprintf(STDERR, "Volume %s already exists\n",
1967                 as->parms[2].items->data);
1968         PrintDiagnostics("create", code);
1969         return EEXIST;
1970     }
1971
1972     if (as->parms[3].items) {
1973         code = util_GetHumanInt32(as->parms[3].items->data, &quota);
1974         if (code) {
1975             fprintf(STDERR, "vos: bad integer specified for quota.\n");
1976             return code;
1977         }
1978     }
1979
1980     if (as->parms[4].items) {
1981         if (!IsNumeric(as->parms[4].items->data)) {
1982             fprintf(STDERR, "vos: Given volume ID %s should be numeric.\n",
1983                     as->parms[4].items->data);
1984             return EINVAL;
1985         }
1986
1987         code = util_GetUInt32(as->parms[4].items->data, &volid);
1988         if (code) {
1989             fprintf(STDERR, "vos: bad integer specified for volume ID.\n");
1990             return code;
1991         }
1992     }
1993
1994     if (as->parms[5].items) {
1995         if (!IsNumeric(as->parms[5].items->data)) {
1996             fprintf(STDERR, "vos: Given RO volume ID %s should be numeric.\n",
1997                     as->parms[5].items->data);
1998             return EINVAL;
1999         }
2000
2001         code = util_GetUInt32(as->parms[5].items->data, &rovolid);
2002         if (code) {
2003             fprintf(STDERR, "vos: bad integer specified for volume ID.\n");
2004             return code;
2005         }
2006
2007         if (rovolid == 0) {
2008             arovolid = NULL;
2009         }
2010     }
2011
2012     code =
2013         UV_CreateVolume3(tserver, pnum, as->parms[2].items->data, quota, 0,
2014                          0, 0, 0, &volid, arovolid, &bkvolid);
2015     if (code) {
2016         PrintDiagnostics("create", code);
2017         return code;
2018     }
2019     MapPartIdIntoName(pnum, part);
2020     fprintf(STDOUT, "Volume %lu created on partition %s of %s\n",
2021             (unsigned long)volid, part, as->parms[0].items->data);
2022
2023     return 0;
2024 }
2025
2026 #if 0
2027 static afs_int32
2028 DeleteAll(struct nvldbentry *entry)
2029 {
2030     int i;
2031     afs_int32 error, code, curserver, curpart;
2032     afs_uint32 volid;
2033
2034     MapHostToNetwork(entry);
2035     error = 0;
2036     for (i = 0; i < entry->nServers; i++) {
2037         curserver = entry->serverNumber[i];
2038         curpart = entry->serverPartition[i];
2039         if (entry->serverFlags[i] & ITSROVOL) {
2040             volid = entry->volumeId[ROVOL];
2041         } else {
2042             volid = entry->volumeId[RWVOL];
2043         }
2044         code = UV_DeleteVolume(curserver, curpart, volid);
2045         if (code && !error)
2046             error = code;
2047     }
2048     return error;
2049 }
2050 #endif
2051
2052 static int
2053 DeleteVolume(struct cmd_syndesc *as, void *arock)
2054 {
2055     afs_int32 err, code = 0;
2056     afs_int32 server = 0, partition = -1;
2057     afs_uint32 volid;
2058     char pname[10];
2059     afs_int32 idx, j;
2060
2061     if (as->parms[0].items) {
2062         server = GetServer(as->parms[0].items->data);
2063         if (!server) {
2064             fprintf(STDERR, "vos: server '%s' not found in host table\n",
2065                     as->parms[0].items->data);
2066             return ENOENT;
2067         }
2068     }
2069
2070     if (as->parms[1].items) {
2071         partition = volutil_GetPartitionID(as->parms[1].items->data);
2072         if (partition < 0) {
2073             fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
2074                     as->parms[1].items->data);
2075             return EINVAL;
2076         }
2077
2078         /* Check for validity of the partition */
2079         if (!IsPartValid(partition, server, &code)) {
2080             if (code) {
2081                 PrintError("", code);
2082             } else {
2083                 fprintf(STDERR,
2084                         "vos : partition %s does not exist on the server\n",
2085                         as->parms[1].items->data);
2086             }
2087             return ENOENT;
2088         }
2089     }
2090
2091     volid = vsu_GetVolumeID(as->parms[2].items->data, cstruct, &err);
2092     if (volid == 0) {
2093         fprintf(STDERR, "Can't find volume name '%s' in VLDB\n",
2094                 as->parms[2].items->data);
2095         if (err)
2096             PrintError("", err);
2097         return ENOENT;
2098     }
2099
2100     /* If the server or partition option are not complete, try to fill
2101      * them in from the VLDB entry.
2102      */
2103     if ((partition == -1) || !server) {
2104         struct nvldbentry entry;
2105
2106         code = VLDB_GetEntryByID(volid, -1, &entry);
2107         if (code) {
2108             fprintf(STDERR,
2109                     "Could not fetch the entry for volume %lu from VLDB\n",
2110                     (unsigned long)volid);
2111             PrintError("", code);
2112             return (code);
2113         }
2114
2115         if (((volid == entry.volumeId[RWVOL]) && (entry.flags & RW_EXISTS))
2116             || ((volid == entry.volumeId[BACKVOL])
2117                 && (entry.flags & BACK_EXISTS))) {
2118             idx = Lp_GetRwIndex(&entry);
2119             if ((idx == -1) || (server && (server != entry.serverNumber[idx]))
2120                 || ((partition != -1)
2121                     && (partition != entry.serverPartition[idx]))) {
2122                 fprintf(STDERR, "VLDB: Volume '%s' no match\n",
2123                         as->parms[2].items->data);
2124                 return ENOENT;
2125             }
2126         } else if ((volid == entry.volumeId[ROVOL])
2127                    && (entry.flags & RO_EXISTS)) {
2128             for (idx = -1, j = 0; j < entry.nServers; j++) {
2129                 if (entry.serverFlags[j] != ITSROVOL)
2130                     continue;
2131
2132                 if (((server == 0) || (server == entry.serverNumber[j]))
2133                     && ((partition == -1)
2134                         || (partition == entry.serverPartition[j]))) {
2135                     if (idx != -1) {
2136                         fprintf(STDERR,
2137                                 "VLDB: Volume '%s' matches more than one RO\n",
2138                                 as->parms[2].items->data);
2139                         return ENOENT;
2140                     }
2141                     idx = j;
2142                 }
2143             }
2144             if (idx == -1) {
2145                 fprintf(STDERR, "VLDB: Volume '%s' no match\n",
2146                         as->parms[2].items->data);
2147                 return ENOENT;
2148             }
2149         } else {
2150             fprintf(STDERR, "VLDB: Volume '%s' no match\n",
2151                     as->parms[2].items->data);
2152             return ENOENT;
2153         }
2154
2155         server = htonl(entry.serverNumber[idx]);
2156         partition = entry.serverPartition[idx];
2157     }
2158
2159
2160     code = UV_DeleteVolume(server, partition, volid);
2161     if (code) {
2162         PrintDiagnostics("remove", code);
2163         return code;
2164     }
2165
2166     MapPartIdIntoName(partition, pname);
2167     fprintf(STDOUT, "Volume %lu on partition %s server %s deleted\n",
2168             (unsigned long)volid, pname, hostutil_GetNameByINet(server));
2169     return 0;
2170 }
2171
2172 #define TESTM   0               /* set for move space tests, clear for production */
2173 static int
2174 MoveVolume(register struct cmd_syndesc *as, void *arock)
2175 {
2176
2177     afs_uint32 volid;
2178     afs_int32 fromserver, toserver, frompart, topart;
2179     afs_int32 flags, code, err;
2180     char fromPartName[10], toPartName[10];
2181
2182     struct diskPartition64 partition;   /* for space check */
2183     volintInfo *p;
2184
2185     volid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err);
2186     if (volid == 0) {
2187         if (err)
2188             PrintError("", err);
2189         else
2190             fprintf(STDERR, "vos: can't find volume ID or name '%s'\n",
2191                     as->parms[0].items->data);
2192         return ENOENT;
2193     }
2194     fromserver = GetServer(as->parms[1].items->data);
2195     if (fromserver == 0) {
2196         fprintf(STDERR, "vos: server '%s' not found in host table\n",
2197                 as->parms[1].items->data);
2198         return ENOENT;
2199     }
2200     toserver = GetServer(as->parms[3].items->data);
2201     if (toserver == 0) {
2202         fprintf(STDERR, "vos: server '%s' not found in host table\n",
2203                 as->parms[3].items->data);
2204         return ENOENT;
2205     }
2206     frompart = volutil_GetPartitionID(as->parms[2].items->data);
2207     if (frompart < 0) {
2208         fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
2209                 as->parms[2].items->data);
2210         return EINVAL;
2211     }
2212     if (!IsPartValid(frompart, fromserver, &code)) {    /*check for validity of the partition */
2213         if (code)
2214             PrintError("", code);
2215         else
2216             fprintf(STDERR,
2217                     "vos : partition %s does not exist on the server\n",
2218                     as->parms[2].items->data);
2219         return ENOENT;
2220     }
2221     topart = volutil_GetPartitionID(as->parms[4].items->data);
2222     if (topart < 0) {
2223         fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
2224                 as->parms[4].items->data);
2225         return EINVAL;
2226     }
2227     if (!IsPartValid(topart, toserver, &code)) {        /*check for validity of the partition */
2228         if (code)
2229             PrintError("", code);
2230         else
2231             fprintf(STDERR,
2232                     "vos : partition %s does not exist on the server\n",
2233                     as->parms[4].items->data);
2234         return ENOENT;
2235     }
2236
2237     flags = 0;
2238     if (as->parms[5].items) flags |= RV_NOCLONE;
2239
2240     /*
2241      * check source partition for space to clone volume
2242      */
2243
2244     MapPartIdIntoName(topart, toPartName);
2245     MapPartIdIntoName(frompart, fromPartName);
2246
2247     /*
2248      * check target partition for space to move volume
2249      */
2250
2251     code = UV_PartitionInfo64(toserver, toPartName, &partition);
2252     if (code) {
2253         fprintf(STDERR, "vos: cannot access partition %s\n", toPartName);
2254         exit(1);
2255     }
2256     if (TESTM)
2257         fprintf(STDOUT, "target partition %s free space %" AFS_INT64_FMT "\n", toPartName,
2258                 partition.free);
2259
2260     p = (volintInfo *) 0;
2261     code = UV_ListOneVolume(fromserver, frompart, volid, &p);
2262     if (code) {
2263         fprintf(STDERR, "vos:cannot access volume %lu\n",
2264                 (unsigned long)volid);
2265         exit(1);
2266     }
2267     if (TESTM)
2268         fprintf(STDOUT, "volume %lu size %d\n", (unsigned long)volid,
2269                 p->size);
2270     if (partition.free <= p->size) {
2271         fprintf(STDERR,
2272                 "vos: no space on target partition %s to move volume %lu\n",
2273                 toPartName, (unsigned long)volid);
2274         free(p);
2275         exit(1);
2276     }
2277     free(p);
2278
2279     if (TESTM) {
2280         fprintf(STDOUT, "size test - don't do move\n");
2281         exit(0);
2282     }
2283
2284     /* successful move still not guaranteed but shoot for it */
2285
2286     code =
2287         UV_MoveVolume2(volid, fromserver, frompart, toserver, topart, flags);
2288     if (code) {
2289         PrintDiagnostics("move", code);
2290         return code;
2291     }
2292     MapPartIdIntoName(topart, toPartName);
2293     MapPartIdIntoName(frompart, fromPartName);
2294     fprintf(STDOUT, "Volume %lu moved from %s %s to %s %s \n",
2295             (unsigned long)volid, as->parms[1].items->data, fromPartName,
2296             as->parms[3].items->data, toPartName);
2297
2298     return 0;
2299 }
2300
2301 static int
2302 CopyVolume(register struct cmd_syndesc *as, void *arock)
2303 {
2304     afs_uint32 volid;
2305     afs_int32 fromserver, toserver, frompart, topart, code, err, flags;
2306     char fromPartName[10], toPartName[10], *tovolume;
2307     struct nvldbentry entry;
2308     struct diskPartition64 partition;   /* for space check */
2309     volintInfo *p;
2310
2311     volid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err);
2312     if (volid == 0) {
2313         if (err)
2314             PrintError("", err);
2315         else
2316             fprintf(STDERR, "vos: can't find volume ID or name '%s'\n",
2317                     as->parms[0].items->data);
2318         return ENOENT;
2319     }
2320     fromserver = GetServer(as->parms[1].items->data);
2321     if (fromserver == 0) {
2322         fprintf(STDERR, "vos: server '%s' not found in host table\n",
2323                 as->parms[1].items->data);
2324         return ENOENT;
2325     }
2326
2327     toserver = GetServer(as->parms[4].items->data);
2328     if (toserver == 0) {
2329         fprintf(STDERR, "vos: server '%s' not found in host table\n",
2330                 as->parms[4].items->data);
2331         return ENOENT;
2332     }
2333
2334     tovolume = as->parms[3].items->data;
2335     if (!ISNAMEVALID(tovolume)) {
2336         fprintf(STDERR,
2337                 "vos: the name of the root volume %s exceeds the size limit of %d\n",
2338                 tovolume, VOLSER_OLDMAXVOLNAME - 10);
2339         return E2BIG;
2340     }
2341     if (!VolNameOK(tovolume)) {
2342         fprintf(STDERR,
2343                 "Illegal volume name %s, should not end in .readonly or .backup\n",
2344                 tovolume);
2345         return EINVAL;
2346     }
2347     if (IsNumeric(tovolume)) {
2348         fprintf(STDERR, "Illegal volume name %s, should not be a number\n",
2349                 tovolume);
2350         return EINVAL;
2351     }
2352     code = VLDB_GetEntryByName(tovolume, &entry);
2353     if (!code) {
2354         fprintf(STDERR, "Volume %s already exists\n", tovolume);
2355         PrintDiagnostics("copy", code);
2356         return EEXIST;
2357     }
2358
2359     frompart = volutil_GetPartitionID(as->parms[2].items->data);
2360     if (frompart < 0) {
2361         fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
2362                 as->parms[2].items->data);
2363         return EINVAL;
2364     }
2365     if (!IsPartValid(frompart, fromserver, &code)) {    /*check for validity of the partition */
2366         if (code)
2367             PrintError("", code);
2368         else
2369             fprintf(STDERR,
2370                     "vos : partition %s does not exist on the server\n",
2371                     as->parms[2].items->data);
2372         return ENOENT;
2373     }
2374
2375     topart = volutil_GetPartitionID(as->parms[5].items->data);
2376     if (topart < 0) {
2377         fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
2378                 as->parms[5].items->data);
2379         return EINVAL;
2380     }
2381     if (!IsPartValid(topart, toserver, &code)) {        /*check for validity of the partition */
2382         if (code)
2383             PrintError("", code);
2384         else
2385             fprintf(STDERR,
2386                     "vos : partition %s does not exist on the server\n",
2387                     as->parms[5].items->data);
2388         return ENOENT;
2389     }
2390
2391     flags = 0;
2392     if (as->parms[6].items) flags |= RV_OFFLINE;
2393     if (as->parms[7].items) flags |= RV_RDONLY;
2394     if (as->parms[8].items) flags |= RV_NOCLONE;
2395
2396     MapPartIdIntoName(topart, toPartName);
2397     MapPartIdIntoName(frompart, fromPartName);
2398
2399     /*
2400      * check target partition for space to move volume
2401      */
2402
2403     code = UV_PartitionInfo64(toserver, toPartName, &partition);
2404     if (code) {
2405         fprintf(STDERR, "vos: cannot access partition %s\n", toPartName);
2406         exit(1);
2407     }
2408     if (TESTM)
2409         fprintf(STDOUT, "target partition %s free space %" AFS_INT64_FMT "\n", toPartName,
2410                 partition.free);
2411
2412     p = (volintInfo *) 0;
2413     code = UV_ListOneVolume(fromserver, frompart, volid, &p);
2414     if (code) {
2415         fprintf(STDERR, "vos:cannot access volume %lu\n",
2416                 (unsigned long)volid);
2417         exit(1);
2418     }
2419
2420     if (partition.free <= p->size) {
2421         fprintf(STDERR,
2422                 "vos: no space on target partition %s to copy volume %lu\n",
2423                 toPartName, (unsigned long)volid);
2424         free(p);
2425         exit(1);
2426     }
2427     free(p);
2428
2429     /* successful copy still not guaranteed but shoot for it */
2430
2431     code =
2432         UV_CopyVolume2(volid, fromserver, frompart, tovolume, toserver,
2433                        topart, 0, flags);
2434     if (code) {
2435         PrintDiagnostics("copy", code);
2436         return code;
2437     }
2438     MapPartIdIntoName(topart, toPartName);
2439     MapPartIdIntoName(frompart, fromPartName);
2440     fprintf(STDOUT, "Volume %lu copied from %s %s to %s on %s %s \n",
2441             (unsigned long)volid, as->parms[1].items->data, fromPartName,
2442             tovolume, as->parms[4].items->data, toPartName);
2443
2444     return 0;
2445 }
2446
2447
2448 static int
2449 ShadowVolume(register struct cmd_syndesc *as, void *arock)
2450 {
2451     afs_uint32 volid, tovolid;
2452     afs_int32 fromserver, toserver, frompart, topart;
2453     afs_int32 code, err, flags;
2454     char fromPartName[10], toPartName[10], toVolName[32], *tovolume;
2455     struct diskPartition64 partition;   /* for space check */
2456     volintInfo *p, *q;
2457
2458     p = (volintInfo *) 0;
2459     q = (volintInfo *) 0;
2460
2461     volid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err);
2462     if (volid == 0) {
2463         if (err)
2464             PrintError("", err);
2465         else
2466             fprintf(STDERR, "vos: can't find volume ID or name '%s'\n",
2467                     as->parms[0].items->data);
2468         return ENOENT;
2469     }
2470     fromserver = GetServer(as->parms[1].items->data);
2471     if (fromserver == 0) {
2472         fprintf(STDERR, "vos: server '%s' not found in host table\n",
2473                 as->parms[1].items->data);
2474         return ENOENT;
2475     }
2476
2477     toserver = GetServer(as->parms[3].items->data);
2478     if (toserver == 0) {
2479         fprintf(STDERR, "vos: server '%s' not found in host table\n",
2480                 as->parms[3].items->data);
2481         return ENOENT;
2482     }
2483
2484     frompart = volutil_GetPartitionID(as->parms[2].items->data);
2485     if (frompart < 0) {
2486         fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
2487                 as->parms[2].items->data);
2488         return EINVAL;
2489     }
2490     if (!IsPartValid(frompart, fromserver, &code)) {    /*check for validity of the partition */
2491         if (code)
2492             PrintError("", code);
2493         else
2494             fprintf(STDERR,
2495                     "vos : partition %s does not exist on the server\n",
2496                     as->parms[2].items->data);
2497         return ENOENT;
2498     }
2499
2500     topart = volutil_GetPartitionID(as->parms[4].items->data);
2501     if (topart < 0) {
2502         fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
2503                 as->parms[4].items->data);
2504         return EINVAL;
2505     }
2506     if (!IsPartValid(topart, toserver, &code)) {        /*check for validity of the partition */
2507         if (code)
2508             PrintError("", code);
2509         else
2510             fprintf(STDERR,
2511                     "vos : partition %s does not exist on the server\n",
2512                     as->parms[4].items->data);
2513         return ENOENT;
2514     }
2515
2516     if (as->parms[5].items) {
2517         tovolume = as->parms[5].items->data;
2518         if (!ISNAMEVALID(tovolume)) {
2519             fprintf(STDERR,
2520                 "vos: the name of the root volume %s exceeds the size limit of %d\n",
2521                 tovolume, VOLSER_OLDMAXVOLNAME - 10);
2522             return E2BIG;
2523         }
2524         if (!VolNameOK(tovolume)) {
2525             fprintf(STDERR,
2526                 "Illegal volume name %s, should not end in .readonly or .backup\n",
2527                 tovolume);
2528             return EINVAL;
2529         }
2530         if (IsNumeric(tovolume)) {
2531             fprintf(STDERR,
2532                 "Illegal volume name %s, should not be a number\n",
2533                 tovolume);
2534             return EINVAL;
2535         }
2536     } else {
2537         /* use actual name of source volume */
2538         code = UV_ListOneVolume(fromserver, frompart, volid, &p);
2539         if (code) {
2540             fprintf(STDERR, "vos:cannot access volume %lu\n",
2541                 (unsigned long)volid);
2542             exit(1);
2543         }
2544         strcpy(toVolName, p->name);
2545         tovolume = toVolName;
2546         /* save p for size checks later */
2547     }
2548
2549     if (as->parms[6].items) {
2550         tovolid = vsu_GetVolumeID(as->parms[6].items->data, cstruct, &err);
2551         if (tovolid == 0) {
2552             if (err)
2553                 PrintError("", err);
2554             else
2555                 fprintf(STDERR, "vos: can't find volume ID or name '%s'\n",
2556                         as->parms[6].items->data);
2557             if (p)
2558                 free(p);
2559             return ENOENT;
2560         }
2561     } else {
2562         tovolid = vsu_GetVolumeID(tovolume, cstruct, &err);
2563         if (tovolid == 0) {
2564             if (err)
2565                 PrintError("", err);
2566             else
2567                 fprintf(STDERR, "vos: can't find volume ID or name '%s'\n",
2568                         tovolume);
2569             if (p)
2570                 free(p);
2571             return ENOENT;
2572         }
2573     }
2574
2575     flags = RV_NOVLDB;
2576     if (as->parms[7].items) flags |= RV_OFFLINE;
2577     if (as->parms[8].items) flags |= RV_RDONLY;
2578     if (as->parms[9].items) flags |= RV_NOCLONE;
2579     if (as->parms[10].items) flags |= RV_CPINCR;
2580
2581     MapPartIdIntoName(topart, toPartName);
2582     MapPartIdIntoName(frompart, fromPartName);
2583
2584     /*
2585      * check target partition for space to move volume
2586      */
2587
2588     code = UV_PartitionInfo64(toserver, toPartName, &partition);
2589     if (code) {
2590         fprintf(STDERR, "vos: cannot access partition %s\n", toPartName);
2591         exit(1);
2592     }
2593     if (TESTM)
2594         fprintf(STDOUT, "target partition %s free space %" AFS_INT64_FMT "\n", toPartName,
2595                 partition.free);
2596
2597     /* Don't do this again if we did it above */
2598     if (!p) {
2599         code = UV_ListOneVolume(fromserver, frompart, volid, &p);
2600         if (code) {
2601             fprintf(STDERR, "vos:cannot access volume %lu\n",
2602                 (unsigned long)volid);
2603             exit(1);
2604         }
2605     }
2606
2607     /* OK if this fails */
2608     code = UV_ListOneVolume(toserver, topart, tovolid, &q);
2609
2610     /* Treat existing volume size as "free" */
2611     if (q)
2612         p->size = (q->size < p->size) ? p->size - q->size : 0;
2613
2614     if (partition.free <= p->size) {
2615         fprintf(STDERR,
2616                 "vos: no space on target partition %s to copy volume %lu\n",
2617                 toPartName, (unsigned long)volid);
2618         free(p);
2619         if (q) free(q);
2620         exit(1);
2621     }
2622     free(p);
2623     if (q) free(q);
2624
2625     /* successful copy still not guaranteed but shoot for it */
2626
2627     code =
2628         UV_CopyVolume2(volid, fromserver, frompart, tovolume, toserver,
2629                        topart, tovolid, flags);
2630     if (code) {
2631         PrintDiagnostics("shadow", code);
2632         return code;
2633     }
2634     MapPartIdIntoName(topart, toPartName);
2635     MapPartIdIntoName(frompart, fromPartName);
2636     fprintf(STDOUT, "Volume %lu shadowed from %s %s to %s %s \n",
2637             (unsigned long)volid, as->parms[1].items->data, fromPartName,
2638             as->parms[3].items->data, toPartName);
2639
2640     return 0;
2641 }
2642
2643
2644 static int
2645 CloneVolume(register struct cmd_syndesc *as, void *arock)
2646 {
2647     afs_uint32 volid, cloneid;
2648     afs_int32 server, part, voltype;
2649     char partName[10], *volname;
2650     afs_int32 code, err, flags;
2651     struct nvldbentry entry;
2652
2653     volid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err);
2654     if (volid == 0) {
2655         if (err)
2656             PrintError("", err);
2657         else
2658             fprintf(STDERR, "vos: can't find volume ID or name '%s'\n",
2659                     as->parms[0].items->data);
2660         return ENOENT;
2661     }
2662
2663     if (as->parms[1].items || as->parms[2].items) {
2664         if (!as->parms[1].items || !as->parms[2].items) {
2665             fprintf(STDERR,
2666                     "Must specify both -server and -partition options\n");
2667             return -1;
2668         }
2669         server = GetServer(as->parms[1].items->data);
2670         if (server == 0) {
2671             fprintf(STDERR, "vos: server '%s' not found in host table\n",
2672                     as->parms[1].items->data);
2673             return ENOENT;
2674         }
2675         part = volutil_GetPartitionID(as->parms[2].items->data);
2676         if (part < 0) {
2677             fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
2678                     as->parms[2].items->data);
2679             return EINVAL;
2680         }
2681         if (!IsPartValid(part, server, &code)) {        /*check for validity of the partition */
2682             if (code)
2683                 PrintError("", code);
2684             else
2685                 fprintf(STDERR,
2686                     "vos : partition %s does not exist on the server\n",
2687                     as->parms[2].items->data);
2688             return ENOENT;
2689         }
2690     } else {
2691         code = GetVolumeInfo(volid, &server, &part, &voltype, &entry);
2692         if (code)
2693             return code;
2694     }
2695
2696     volname = 0;
2697     if (as->parms[3].items) {
2698         volname = as->parms[3].items->data;
2699         if (strlen(volname) > VOLSER_OLDMAXVOLNAME - 1) {
2700             fprintf(STDERR,
2701                 "vos: the name of the root volume %s exceeds the size limit of %d\n",
2702                 volname, VOLSER_OLDMAXVOLNAME - 1);
2703             return E2BIG;
2704         }
2705 #if 0
2706         /* 
2707          * In order that you be able to make clones of RO or BK, this
2708          * check must be omitted.
2709          */
2710         if (!VolNameOK(volname)) {
2711             fprintf(STDERR,
2712                 "Illegal volume name %s, should not end in .readonly or .backup\n",
2713                 volname);
2714             return EINVAL;
2715         }
2716 #endif
2717         if (IsNumeric(volname)) {
2718             fprintf(STDERR,
2719                 "Illegal volume name %s, should not be a number\n",
2720                 volname);
2721             return EINVAL;
2722         }
2723     }
2724
2725     cloneid = 0;
2726     if (as->parms[4].items) {
2727         cloneid = vsu_GetVolumeID(as->parms[4].items->data, cstruct, &err);
2728         if (cloneid == 0) {
2729             if (err)
2730                 PrintError("", err);
2731             else
2732                 fprintf(STDERR, "vos: can't find volume ID or name '%s'\n",
2733                         as->parms[4].items->data);
2734             return ENOENT;
2735         }
2736     }
2737
2738     flags = 0;
2739     if (as->parms[5].items) flags |= RV_OFFLINE;
2740     if (as->parms[6].items) flags |= RV_RDONLY;
2741
2742
2743     code = 
2744         UV_CloneVolume(server, part, volid, cloneid, volname, flags);
2745
2746     if (code) {
2747         PrintDiagnostics("clone", code);
2748         return code;
2749     }
2750     MapPartIdIntoName(part, partName);
2751     fprintf(STDOUT, "Created clone for volume %s\n",
2752             as->parms[0].items->data);
2753
2754     return 0;
2755 }
2756
2757
2758 static int
2759 BackupVolume(register struct cmd_syndesc *as, void *arock)
2760 {
2761     afs_uint32 avolid;
2762     afs_int32 aserver, apart, vtype, code, err;
2763     struct nvldbentry entry;
2764
2765     afs_uint32 buvolid;
2766     afs_int32 buserver, bupart, butype;
2767     struct nvldbentry buentry;
2768
2769     avolid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err);
2770     if (avolid == 0) {
2771         if (err)
2772             PrintError("", err);
2773         else
2774             fprintf(STDERR, "vos: can't find volume ID or name '%s'\n",
2775                     as->parms[0].items->data);
2776         return ENOENT;
2777     }
2778     code = GetVolumeInfo(avolid, &aserver, &apart, &vtype, &entry);
2779     if (code)
2780         exit(1);
2781
2782     /* verify this is a readwrite volume */
2783
2784     if (vtype != RWVOL) {
2785         fprintf(STDERR, "%s not RW volume\n", as->parms[0].items->data);
2786         exit(1);
2787     }
2788
2789     /* is there a backup volume already? */
2790
2791     if (entry.flags & BACK_EXISTS) {
2792         /* yep, where is it? */
2793
2794         buvolid = entry.volumeId[BACKVOL];
2795         code = GetVolumeInfo(buvolid, &buserver, &bupart, &butype, &buentry);
2796         if (code)
2797             exit(1);
2798
2799         /* is it local? */
2800         code = VLDB_IsSameAddrs(buserver, aserver, &err);
2801         if (err) {
2802             fprintf(STDERR,
2803                     "Failed to get info about server's %d address(es) from vlserver; aborting call!\n",
2804                     buserver);
2805             exit(1);
2806         }
2807         if (!code) {
2808             fprintf(STDERR,
2809                     "FATAL ERROR: backup volume %lu exists on server %lu\n",
2810                     (unsigned long)buvolid, (unsigned long)buserver);
2811             exit(1);
2812         }
2813     }
2814
2815     /* nope, carry on */
2816
2817     code = UV_BackupVolume(aserver, apart, avolid);
2818
2819     if (code) {
2820         PrintDiagnostics("backup", code);
2821         return code;
2822     }
2823     fprintf(STDOUT, "Created backup volume for %s \n",
2824             as->parms[0].items->data);
2825     return 0;
2826 }
2827
2828 static int
2829 ReleaseVolume(register struct cmd_syndesc *as, void *arock)
2830 {
2831
2832     struct nvldbentry entry;
2833     afs_uint32 avolid;
2834     afs_int32 aserver, apart, vtype, code, err;
2835     int force = 0;
2836
2837     if (as->parms[1].items)
2838         force = 1;
2839     avolid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err);
2840     if (avolid == 0) {
2841         if (err)
2842             PrintError("", err);
2843         else
2844             fprintf(STDERR, "vos: can't find volume '%s'\n",
2845                     as->parms[0].items->data);
2846         return ENOENT;
2847     }
2848     code = GetVolumeInfo(avolid, &aserver, &apart, &vtype, &entry);
2849     if (code)
2850         return code;
2851
2852     if (vtype != RWVOL) {
2853         fprintf(STDERR, "%s not a RW volume\n", as->parms[0].items->data);
2854         return (ENOENT);
2855     }
2856
2857     if (!ISNAMEVALID(entry.name)) {
2858         fprintf(STDERR,
2859                 "Volume name %s is too long, rename before releasing\n",
2860                 entry.name);
2861         return E2BIG;
2862     }
2863
2864     code = UV_ReleaseVolume(avolid, aserver, apart, force);
2865     if (code) {
2866         PrintDiagnostics("release", code);
2867         return code;
2868     }
2869     fprintf(STDOUT, "Released volume %s successfully\n",
2870             as->parms[0].items->data);
2871     return 0;
2872 }
2873
2874 static int
2875 DumpVolumeCmd(register struct cmd_syndesc *as, void *arock)
2876 {
2877     afs_uint32 avolid;
2878     afs_int32 aserver, apart, voltype, fromdate = 0, code, err, i, flags;
2879     char filename[MAXPATHLEN];
2880     struct nvldbentry entry;
2881
2882     rx_SetRxDeadTime(60 * 10);
2883     for (i = 0; i < MAXSERVERS; i++) {
2884         struct rx_connection *rxConn = ubik_GetRPCConn(cstruct, i);
2885         if (rxConn == 0)
2886             break;
2887         rx_SetConnDeadTime(rxConn, rx_connDeadTime);
2888         if (rxConn->service)
2889             rxConn->service->connDeadTime = rx_connDeadTime;
2890     }
2891
2892     avolid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err);
2893     if (avolid == 0) {
2894         if (err)
2895             PrintError("", err);
2896         else
2897             fprintf(STDERR, "vos: can't find volume '%s'\n",
2898                     as->parms[0].items->data);
2899         return ENOENT;
2900     }
2901
2902     if (as->parms[3].items || as->parms[4].items) {
2903         if (!as->parms[3].items || !as->parms[4].items) {
2904             fprintf(STDERR,
2905                     "Must specify both -server and -partition options\n");
2906             return -1;
2907         }
2908         aserver = GetServer(as->parms[3].items->data);
2909         if (aserver == 0) {
2910             fprintf(STDERR, "Invalid server name\n");
2911             return -1;
2912         }
2913         apart = volutil_GetPartitionID(as->parms[4].items->data);
2914         if (apart < 0) {
2915             fprintf(STDERR, "Invalid partition name\n");
2916             return -1;
2917         }
2918     } else {
2919         code = GetVolumeInfo(avolid, &aserver, &apart, &voltype, &entry);
2920         if (code)
2921             return code;
2922     }
2923
2924     if (as->parms[1].items && strcmp(as->parms[1].items->data, "0")) {
2925         code = ktime_DateToInt32(as->parms[1].items->data, &fromdate);
2926         if (code) {
2927             fprintf(STDERR, "vos: failed to parse date '%s' (error=%d))\n",
2928                     as->parms[1].items->data, code);
2929             return code;
2930         }
2931     }
2932     if (as->parms[2].items) {
2933         strcpy(filename, as->parms[2].items->data);
2934     } else {
2935         strcpy(filename, "");
2936     }
2937
2938     flags = as->parms[6].items ? VOLDUMPV2_OMITDIRS : 0;
2939 retry_dump:
2940     if (as->parms[5].items) {
2941         code =
2942             UV_DumpClonedVolume(avolid, aserver, apart, fromdate,
2943                                 DumpFunction, filename, flags);
2944     } else {
2945         code =
2946             UV_DumpVolume(avolid, aserver, apart, fromdate, DumpFunction,
2947                           filename, flags);
2948     }
2949     if ((code == RXGEN_OPCODE) && (as->parms[6].items)) {
2950         flags &= ~VOLDUMPV2_OMITDIRS;
2951         goto retry_dump;
2952     }
2953     if (code) {
2954         PrintDiagnostics("dump", code);
2955         return code;
2956     }
2957     if (strcmp(filename, ""))
2958         fprintf(STDERR, "Dumped volume %s in file %s\n",
2959                 as->parms[0].items->data, filename);
2960     else
2961         fprintf(STDERR, "Dumped volume %s in stdout \n",
2962                 as->parms[0].items->data);
2963     return 0;
2964 }
2965
2966 #define ASK   0
2967 #define ABORT 1
2968 #define FULL  2
2969 #define INC   3
2970
2971 #define TS_DUMP 1
2972 #define TS_KEEP 2
2973 #define TS_NEW  3
2974
2975 static int
2976 RestoreVolumeCmd(register struct cmd_syndesc *as, void *arock)
2977 {
2978     afs_uint32 avolid, aparentid;
2979     afs_int32 aserver, apart, code, vcode, err;
2980     afs_int32 aoverwrite = ASK;
2981     afs_int32 acreation = 0, alastupdate = 0;
2982     int restoreflags = 0;
2983     int readonly = 0, offline = 0, voltype = RWVOL;
2984     char prompt;
2985     char afilename[MAXPATHLEN], avolname[VOLSER_MAXVOLNAME + 1], apartName[10];
2986     char volname[VOLSER_MAXVOLNAME + 1];
2987     struct nvldbentry entry;
2988
2989     prompt = 'n';
2990
2991     aparentid = 0;
2992     if (as->parms[4].items) {
2993         avolid = vsu_GetVolumeID(as->parms[4].items->data, cstruct, &err);
2994         if (avolid == 0) {
2995             if (err)
2996                 PrintError("", err);
2997             else
2998                 fprintf(STDERR, "vos: can't find volume '%s'\n",
2999                         as->parms[4].items->data);
3000             exit(1);
3001         }
3002     } else
3003         avolid = 0;
3004
3005     if (as->parms[5].items) {
3006         if ((strcmp(as->parms[5].items->data, "a") == 0)
3007             || (strcmp(as->parms[5].items->data, "abort") == 0)) {
3008             aoverwrite = ABORT;
3009         } else if ((strcmp(as->parms[5].items->data, "f") == 0)
3010                    || (strcmp(as->parms[5].items->data, "full") == 0)) {
3011             aoverwrite = FULL;
3012         } else if ((strcmp(as->parms[5].items->data, "i") == 0)
3013                    || (strcmp(as->parms[5].items->data, "inc") == 0)
3014                    || (strcmp(as->parms[5].items->data, "increment") == 0)
3015                    || (strcmp(as->parms[5].items->data, "incremental") == 0)) {
3016             aoverwrite = INC;
3017         } else {
3018             fprintf(STDERR, "vos: %s is not a valid argument to -overwrite\n",
3019                     as->parms[5].items->data);
3020             exit(1);
3021         }
3022     }
3023     if (as->parms[6].items)
3024         offline = 1;
3025     if (as->parms[7].items) {
3026         readonly = 1;
3027         voltype = ROVOL;
3028     }
3029
3030     if (as->parms[8].items) {
3031         if ((strcmp(as->parms[8].items->data, "d") == 0)
3032             || (strcmp(as->parms[8].items->data, "dump") == 0)) {
3033             acreation = TS_DUMP;
3034         } else if ((strcmp(as->parms[8].items->data, "k") == 0)
3035             || (strcmp(as->parms[8].items->data, "keep") == 0)) {
3036             acreation = TS_KEEP;
3037         } else if ((strcmp(as->parms[8].items->data, "n") == 0)
3038             || (strcmp(as->parms[8].items->data, "new") == 0)) {
3039             acreation = TS_NEW;
3040         } else {
3041             fprintf(STDERR, "vos: %s is not a valid argument to -creation\n",
3042                     as->parms[8].items->data);
3043             exit(1);
3044         }
3045     }
3046
3047     if (as->parms[9].items) {
3048         if ((strcmp(as->parms[9].items->data, "d") == 0)
3049             || (strcmp(as->parms[9].items->data, "dump") == 0)) {
3050             alastupdate = TS_DUMP;
3051         } else if ((strcmp(as->parms[9].items->data, "k") == 0)
3052             || (strcmp(as->parms[9].items->data, "keep") == 0)) {
3053             alastupdate = TS_KEEP;
3054         } else if ((strcmp(as->parms[9].items->data, "n") == 0)
3055             || (strcmp(as->parms[9].items->data, "new") == 0)) {
3056             alastupdate = TS_NEW;
3057         } else {
3058             fprintf(STDERR, "vos: %s is not a valid argument to -lastupdate\n",
3059                     as->parms[9].items->data);
3060             exit(1);
3061         }
3062     }
3063
3064     aserver = GetServer(as->parms[0].items->data);
3065     if (aserver == 0) {
3066         fprintf(STDERR, "vos: server '%s' not found in host table\n",
3067                 as->parms[0].items->data);
3068         exit(1);
3069     }
3070     apart = volutil_GetPartitionID(as->parms[1].items->data);
3071     if (apart < 0) {
3072         fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
3073                 as->parms[1].items->data);
3074         exit(1);
3075     }
3076     if (!IsPartValid(apart, aserver, &code)) {  /*check for validity of the partition */
3077         if (code)
3078             PrintError("", code);
3079         else
3080             fprintf(STDERR,
3081                     "vos : partition %s does not exist on the server\n",
3082                     as->parms[1].items->data);
3083         exit(1);
3084     }
3085     strcpy(avolname, as->parms[2].items->data);
3086     if (!ISNAMEVALID(avolname)) {
3087         fprintf(STDERR,
3088                 "vos: the name of the volume %s exceeds the size limit\n",
3089                 avolname);
3090         exit(1);
3091     }
3092     if (!VolNameOK(avolname)) {
3093         fprintf(STDERR,
3094                 "Illegal volume name %s, should not end in .readonly or .backup\n",
3095                 avolname);
3096         exit(1);
3097     }
3098     if (as->parms[3].items) {
3099         strcpy(afilename, as->parms[3].items->data);
3100         if (!FileExists(afilename)) {
3101             fprintf(STDERR, "Can't access file %s\n", afilename);
3102             exit(1);
3103         }
3104     } else {
3105         strcpy(afilename, "");
3106     }
3107
3108     /* Check if volume exists or not */
3109
3110     vsu_ExtractName(volname, avolname);
3111     vcode = VLDB_GetEntryByName(volname, &entry);
3112     if (vcode) {                /* no volume - do a full restore */
3113         restoreflags = RV_FULLRST;
3114         if ((aoverwrite == INC) || (aoverwrite == ABORT))
3115             fprintf(STDERR,
3116                     "Volume does not exist; Will perform a full restore\n");
3117     }
3118
3119     else if ((!readonly && Lp_GetRwIndex(&entry) == -1) /* RW volume does not exist - do a full */
3120              ||(readonly && !Lp_ROMatch(0, 0, &entry))) {       /* RO volume does not exist - do a full */
3121         restoreflags = RV_FULLRST;
3122         if ((aoverwrite == INC) || (aoverwrite == ABORT))
3123             fprintf(STDERR,
3124                     "%s Volume does not exist; Will perform a full restore\n",
3125                     readonly ? "RO" : "RW");
3126
3127         if (avolid == 0) {
3128             avolid = entry.volumeId[voltype];
3129         } else if (entry.volumeId[voltype] != 0
3130                    && entry.volumeId[voltype] != avolid) {
3131             avolid = entry.volumeId[voltype];
3132         }
3133         aparentid = entry.volumeId[RWVOL];
3134     }
3135
3136     else {                      /* volume exists - do we do a full incremental or abort */
3137         int Oserver, Opart, Otype, vol_elsewhere = 0;
3138         struct nvldbentry Oentry;
3139         int c, dc;
3140
3141         if (avolid == 0) {
3142             avolid = entry.volumeId[voltype];
3143         } else if (entry.volumeId[voltype] != 0
3144                    && entry.volumeId[voltype] != avolid) {
3145             avolid = entry.volumeId[voltype];
3146         }
3147         aparentid = entry.volumeId[RWVOL];
3148
3149         /* A file name was specified  - check if volume is on another partition */
3150         vcode = GetVolumeInfo(avolid, &Oserver, &Opart, &Otype, &Oentry);
3151         if (vcode)
3152             exit(1);
3153
3154         vcode = VLDB_IsSameAddrs(Oserver, aserver, &err);
3155         if (err) {
3156             fprintf(STDERR,
3157                     "Failed to get info about server's %d address(es) from vlserver (err=%d); aborting call!\n",
3158                     Oserver, err);
3159             exit(1);
3160         }
3161         if (!vcode || (Opart != apart))
3162             vol_elsewhere = 1;
3163
3164         if (aoverwrite == ASK) {
3165             if (strcmp(afilename, "") == 0) {   /* The file is from standard in */
3166                 fprintf(STDERR,
3167                         "Volume exists and no -overwrite option specified; Aborting restore command\n");
3168                 exit(1);
3169             }
3170
3171             /* Ask what to do */
3172             if (vol_elsewhere) {
3173                 fprintf(STDERR,
3174                         "The volume %s %u already exists on a different server/part\n",
3175                         volname, entry.volumeId[voltype]);
3176                 fprintf(STDERR,
3177                         "Do you want to do a full restore or abort? [fa](a): ");
3178             } else {
3179                 fprintf(STDERR,
3180                         "The volume %s %u already exists in the VLDB\n",
3181                         volname, entry.volumeId[voltype]);
3182                 fprintf(STDERR,
3183                         "Do you want to do a full/incremental restore or abort? [fia](a): ");
3184             }
3185             dc = c = getchar();
3186             while (!(dc == EOF || dc == '\n'))
3187                 dc = getchar(); /* goto end of line */
3188             if ((c == 'f') || (c == 'F'))
3189                 aoverwrite = FULL;
3190             else if ((c == 'i') || (c == 'I'))
3191                 aoverwrite = INC;
3192             else
3193                 aoverwrite = ABORT;
3194         }
3195
3196         if (aoverwrite == ABORT) {
3197             fprintf(STDERR, "Volume exists; Aborting restore command\n");
3198             exit(1);
3199         } else if (aoverwrite == FULL) {
3200             restoreflags = RV_FULLRST;
3201             fprintf(STDERR,
3202                     "Volume exists; Will delete and perform full restore\n");
3203         } else if (aoverwrite == INC) {
3204             restoreflags = 0;
3205             if (vol_elsewhere) {
3206                 fprintf(STDERR,
3207                         "%s volume %lu already exists on a different server/part; not allowed\n",
3208                         readonly ? "RO" : "RW", (unsigned long)avolid);
3209                 exit(1);
3210             }
3211         }
3212     }
3213     if (offline)
3214         restoreflags |= RV_OFFLINE;
3215     if (readonly)
3216         restoreflags |= RV_RDONLY;
3217
3218     switch (acreation) {
3219         case TS_DUMP:
3220             restoreflags |= RV_CRDUMP;
3221             break;
3222         case TS_KEEP:
3223             restoreflags |= RV_CRKEEP;
3224             break;
3225         case TS_NEW:
3226             restoreflags |= RV_CRNEW;
3227             break;
3228         default:
3229             if (aoverwrite == FULL)
3230                 restoreflags |= RV_CRNEW;
3231             else
3232                 restoreflags |= RV_CRKEEP;
3233     }
3234
3235     switch (alastupdate) {
3236         case TS_DUMP:
3237             restoreflags |= RV_LUDUMP;
3238             break;
3239         case TS_KEEP:
3240             restoreflags |= RV_LUKEEP;
3241             break;
3242         case TS_NEW:
3243             restoreflags |= RV_LUNEW;
3244             break;
3245         default:
3246             restoreflags |= RV_LUDUMP;
3247     }
3248     if (as->parms[10].items) {
3249         restoreflags |= RV_NODEL;
3250     }
3251     
3252
3253     code =
3254         UV_RestoreVolume2(aserver, apart, avolid, aparentid,
3255                           avolname, restoreflags, WriteData, afilename);
3256     if (code) {
3257         PrintDiagnostics("restore", code);
3258         exit(1);
3259     }
3260     MapPartIdIntoName(apart, apartName);
3261
3262     /*
3263      * patch typo here - originally "parms[1]", should be "parms[0]"
3264      */
3265
3266     fprintf(STDOUT, "Restored volume %s on %s %s\n", avolname,
3267             as->parms[0].items->data, apartName);
3268     return 0;
3269 }
3270
3271 static int
3272 LockReleaseCmd(register struct cmd_syndesc *as, void *arock)
3273 {
3274     afs_uint32 avolid;
3275     afs_int32 code, err;
3276
3277     avolid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err);
3278     if (avolid == 0) {
3279         if (err)
3280             PrintError("", err);
3281         else
3282             fprintf(STDERR, "vos: can't find volume '%s'\n",
3283                     as->parms[0].items->data);
3284         exit(1);
3285     }
3286
3287     code = UV_LockRelease(avolid);
3288     if (code) {
3289         PrintDiagnostics("unlock", code);
3290         exit(1);
3291     }
3292     fprintf(STDOUT, "Released lock on vldb entry for volume %s\n",
3293             as->parms[0].items->data);
3294     return 0;
3295 }
3296
3297 static int
3298 AddSite(register struct cmd_syndesc *as, void *arock)
3299 {
3300     afs_uint32 avolid;
3301     afs_int32 aserver, apart, code, err, arovolid, valid = 0;
3302     char apartName[10], avolname[VOLSER_MAXVOLNAME + 1];
3303
3304     vsu_ExtractName(avolname, as->parms[2].items->data);;
3305     avolid = vsu_GetVolumeID(avolname, cstruct, &err);
3306     if (avolid == 0) {
3307         if (err)
3308             PrintError("", err);
3309         else
3310             fprintf(STDERR, "vos: can't find volume '%s'\n",
3311                     as->parms[2].items->data);
3312         exit(1);
3313     }
3314     arovolid = 0;
3315     if (as->parms[3].items) {
3316         vsu_ExtractName(avolname, as->parms[3].items->data);
3317         arovolid = vsu_GetVolumeID(avolname, cstruct, &err);
3318         if (!arovolid) {
3319             fprintf(STDERR, "vos: invalid ro volume id '%s'\n",
3320                     as->parms[3].items->data);
3321             exit(1);
3322         }
3323     }
3324     aserver = GetServer(as->parms[0].items->data);
3325     if (aserver == 0) {
3326         fprintf(STDERR, "vos: server '%s' not found in host table\n",
3327                 as->parms[0].items->data);
3328         exit(1);
3329     }
3330     apart = volutil_GetPartitionID(as->parms[1].items->data);
3331     if (apart < 0) {
3332         fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
3333                 as->parms[1].items->data);
3334         exit(1);
3335     }
3336     if (!IsPartValid(apart, aserver, &code)) {  /*check for validity of the partition */
3337         if (code)
3338             PrintError("", code);
3339         else
3340             fprintf(STDERR,
3341                     "vos : partition %s does not exist on the server\n",
3342                     as->parms[1].items->data);
3343         exit(1);
3344     }
3345     if (as->parms[4].items) {
3346         valid = 1;
3347     }
3348     code = UV_AddSite2(aserver, apart, avolid, arovolid, valid);
3349     if (code) {
3350         PrintDiagnostics("addsite", code);
3351         exit(1);
3352     }
3353     MapPartIdIntoName(apart, apartName);
3354     fprintf(STDOUT, "Added replication site %s %s for volume %s\n",
3355             as->parms[0].items->data, apartName, as->parms[2].items->data);
3356     return 0;
3357 }
3358
3359 static int
3360 RemoveSite(register struct cmd_syndesc *as, void *arock)
3361 {
3362
3363     afs_uint32 avolid;
3364     afs_int32 aserver, apart, code, err;
3365     char apartName[10], avolname[VOLSER_MAXVOLNAME + 1];
3366
3367     vsu_ExtractName(avolname, as->parms[2].items->data);
3368     avolid = vsu_GetVolumeID(avolname, cstruct, &err);
3369     if (avolid == 0) {
3370         if (err)
3371             PrintError("", err);
3372         else
3373             fprintf(STDERR, "vos: can't find volume '%s'\n",
3374                     as->parms[2].items->data);
3375         exit(1);
3376     }
3377     aserver = GetServer(as->parms[0].items->data);
3378     if (aserver == 0) {
3379         fprintf(STDERR, "vos: server '%s' not found in host table\n",
3380                 as->parms[0].items->data);
3381         exit(1);
3382     }
3383     apart = volutil_GetPartitionID(as->parms[1].items->data);
3384     if (apart < 0) {
3385         fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
3386                 as->parms[1].items->data);
3387         exit(1);
3388     }
3389 /*
3390  *skip the partition validity check, since it is possible that the partition
3391  *has since been decomissioned.
3392  */
3393 /*
3394         if (!IsPartValid(apart,aserver,&code)){
3395             if(code) PrintError("",code);
3396             else fprintf(STDERR,"vos : partition %s does not exist on the server\n",as->parms[1].items->data);
3397             exit(1);
3398         }
3399 */
3400     code = UV_RemoveSite(aserver, apart, avolid);
3401     if (code) {
3402         PrintDiagnostics("remsite", code);
3403         exit(1);
3404     }
3405     MapPartIdIntoName(apart, apartName);
3406     fprintf(STDOUT, "Removed replication site %s %s for volume %s\n",
3407             as->parms[0].items->data, apartName, as->parms[2].items->data);
3408     return 0;
3409 }
3410
3411 static int
3412 ChangeLocation(register struct cmd_syndesc *as, void *arock)
3413 {
3414     afs_uint32 avolid;
3415     afs_int32 aserver, apart, code, err;
3416     char apartName[10];
3417
3418     avolid = vsu_GetVolumeID(as->parms[2].items->data, cstruct, &err);
3419     if (avolid == 0) {
3420         if (err)
3421             PrintError("", err);
3422         else
3423             fprintf(STDERR, "vos: can't find volume '%s'\n",
3424                     as->parms[2].items->data);
3425         exit(1);
3426     }
3427     aserver = GetServer(as->parms[0].items->data);
3428     if (aserver == 0) {
3429         fprintf(STDERR, "vos: server '%s' not found in host table\n",
3430                 as->parms[0].items->data);
3431         exit(1);
3432     }
3433     apart = volutil_GetPartitionID(as->parms[1].items->data);
3434     if (apart < 0) {
3435         fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
3436                 as->parms[1].items->data);
3437         exit(1);
3438     }
3439     if (!IsPartValid(apart, aserver, &code)) {  /*check for validity of the partition */
3440         if (code)
3441             PrintError("", code);
3442         else
3443             fprintf(STDERR,
3444                     "vos : partition %s does not exist on the server\n",
3445                     as->parms[1].items->data);
3446         exit(1);
3447     }
3448     code = UV_ChangeLocation(aserver, apart, avolid);
3449     if (code) {
3450         PrintDiagnostics("addsite", code);
3451         exit(1);
3452     }
3453     MapPartIdIntoName(apart, apartName);
3454     fprintf(STDOUT, "Changed location to %s %s for volume %s\n",
3455             as->parms[0].items->data, apartName, as->parms[2].items->data);
3456     return 0;
3457 }
3458
3459 static int
3460 ListPartitions(register struct cmd_syndesc *as, void *arock)
3461 {
3462     afs_int32 aserver, code;
3463     struct partList dummyPartList;
3464     int i;
3465     char pname[10];
3466     int total, cnt;
3467
3468     aserver = GetServer(as->parms[0].items->data);
3469     if (aserver == 0) {
3470         fprintf(STDERR, "vos: server '%s' not found in host table\n",
3471                 as->parms[0].items->data);
3472         exit(1);
3473     }
3474
3475
3476     code = UV_ListPartitions(aserver, &dummyPartList, &cnt);
3477     if (code) {
3478         PrintDiagnostics("listpart", code);
3479         exit(1);
3480     }
3481     total = 0;
3482     fprintf(STDOUT, "The partitions on the server are:\n");
3483     for (i = 0; i < cnt; i++) {
3484         if (dummyPartList.partFlags[i] & PARTVALID) {
3485             memset(pname, 0, sizeof(pname));
3486             MapPartIdIntoName(dummyPartList.partId[i], pname);
3487             fprintf(STDOUT, " %10s ", pname);
3488             total++;
3489             if ((i % 5) == 0 && (i != 0))
3490                 fprintf(STDOUT, "\n");
3491         }
3492     }
3493     fprintf(STDOUT, "\n");
3494     fprintf(STDOUT, "Total: %d\n", total);
3495     return 0;
3496
3497 }
3498
3499 static int
3500 CompareVolName(const void *p1, const void *p2)
3501 {
3502     volintInfo *arg1, *arg2;
3503
3504     arg1 = (volintInfo *) p1;
3505     arg2 = (volintInfo *) p2;
3506     return (strcmp(arg1->name, arg2->name));
3507
3508 }
3509
3510 /*------------------------------------------------------------------------
3511  * PRIVATE XCompareVolName
3512  *
3513  * Description:
3514  *      Comparison routine for volume names coming from an extended
3515  *      volume listing.
3516  *
3517  * Arguments:
3518  *      a_obj1P : Char ptr to first extended vol info object
3519  *      a_obj1P : Char ptr to second extended vol info object
3520  *
3521  * Returns:
3522  *      The value of strcmp() on the volume names within the passed
3523  *      objects (i,e., -1, 0, or 1).
3524  *
3525  * Environment:
3526  *      Passed to qsort() as the designated comparison routine.
3527  *
3528  * Side Effects:
3529  *      As advertised.
3530  *------------------------------------------------------------------------*/
3531
3532 static int
3533 XCompareVolName(const void *a_obj1P, const void *a_obj2P)
3534 {                               /*XCompareVolName */
3535
3536     return (strcmp
3537             (((struct volintXInfo *)(a_obj1P))->name,
3538              ((struct volintXInfo *)(a_obj2P))->name));
3539
3540 }                               /*XCompareVolName */
3541
3542 static int
3543 CompareVolID(const void *p1, const void *p2)
3544 {
3545     volintInfo *arg1, *arg2;
3546
3547     arg1 = (volintInfo *) p1;
3548     arg2 = (volintInfo *) p2;
3549     if (arg1->volid == arg2->volid)
3550         return 0;
3551     if (arg1->volid > arg2->volid)
3552         return 1;
3553     else
3554         return -1;
3555
3556 }
3557
3558 /*------------------------------------------------------------------------
3559  * PRIVATE XCompareVolID
3560  *
3561  * Description:
3562  *      Comparison routine for volume IDs coming from an extended
3563  *      volume listing.
3564  *
3565  * Arguments:
3566  *      a_obj1P : Char ptr to first extended vol info object
3567  *      a_obj1P : Char ptr to second extended vol info object
3568  *
3569  * Returns:
3570  *      The value of strcmp() on the volume names within the passed
3571  *      objects (i,e., -1, 0, or 1).
3572  *
3573  * Environment:
3574  *      Passed to qsort() as the designated comparison routine.
3575  *
3576  * Side Effects:
3577  *      As advertised.
3578  *------------------------------------------------------------------------*/
3579
3580 static int
3581 XCompareVolID(const void *a_obj1P, const void *a_obj2P)
3582 {                               /*XCompareVolID */
3583
3584     afs_int32 id1, id2;         /*Volume IDs we're comparing */
3585
3586     id1 = ((struct volintXInfo *)(a_obj1P))->volid;
3587     id2 = ((struct volintXInfo *)(a_obj2P))->volid;
3588     if (id1 == id2)
3589         return (0);
3590     else if (id1 > id2)
3591         return (1);
3592     else
3593         return (-1);
3594
3595 }                               /*XCompareVolID */
3596
3597 /*------------------------------------------------------------------------
3598  * PRIVATE ListVolumes
3599  *
3600  * Description:
3601  *      Routine used to list volumes, contacting the Volume Server
3602  *      directly, bypassing the VLDB.
3603  *
3604  * Arguments:
3605  *      as : Ptr to parsed command line arguments.
3606  *
3607  * Returns:
3608  *      0                       Successful operation
3609  *
3610  * Environment:
3611  *      Nothing interesting.
3612  *
3613  * Side Effects:
3614  *      As advertised.
3615  *------------------------------------------------------------------------*/
3616
3617 static int
3618 ListVolumes(register struct cmd_syndesc *as, void *arock)
3619 {
3620     afs_int32 apart, int32list, fast;
3621     afs_int32 aserver, code;
3622     volintInfo *pntr;
3623     volintInfo *oldpntr = NULL;
3624     afs_int32 count;
3625     int i;
3626     char *base;
3627     volintXInfo *xInfoP;
3628     volintXInfo *origxInfoP = NULL; /*Ptr to current/orig extended vol info */
3629     int wantExtendedInfo;       /*Do we want extended vol info? */
3630
3631     char pname[10];
3632     struct partList dummyPartList;
3633     int all;
3634     int quiet, cnt;
3635
3636     apart = -1;
3637     fast = 0;
3638     int32list = 0;
3639
3640     if (as->parms[3].items)
3641         int32list = 1;
3642     if (as->parms[4].items)
3643         quiet = 1;
3644     else
3645         quiet = 0;
3646     if (as->parms[2].items)
3647         fast = 1;
3648     if (fast)
3649         all = 0;
3650     else
3651         all = 1;
3652     if (as->parms[5].items) {
3653         /*
3654          * We can't coexist with the fast flag.
3655          */
3656         if (fast) {
3657             fprintf(STDERR,
3658                     "vos: Can't use the -fast and -extended flags together\n");
3659             exit(1);
3660         }
3661
3662         /*
3663          * We need to turn on ``long'' listings to get the full effect.
3664          */
3665         wantExtendedInfo = 1;
3666         int32list = 1;
3667     } else
3668         wantExtendedInfo = 0;
3669     if (as->parms[1].items) {
3670         apart = volutil_GetPartitionID(as->parms[1].items->data);
3671         if (apart < 0) {
3672             fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
3673                     as->parms[1].items->data);
3674             exit(1);
3675         }
3676         dummyPartList.partId[0] = apart;
3677         dummyPartList.partFlags[0] = PARTVALID;
3678         cnt = 1;
3679     }
3680     aserver = GetServer(as->parms[0].items->data);
3681     if (aserver == 0) {
3682         fprintf(STDERR, "vos: server '%s' not found in host table\n",
3683                 as->parms[0].items->data);
3684         exit(1);
3685     }
3686
3687     if (apart != -1) {
3688         if (!IsPartValid(apart, aserver, &code)) {      /*check for validity of the partition */
3689             if (code)
3690                 PrintError("", code);
3691             else
3692                 fprintf(STDERR,
3693                         "vos : partition %s does not exist on the server\n",
3694                         as->parms[1].items->data);
3695             exit(1);
3696         }
3697     } else {
3698         code = UV_ListPartitions(aserver, &dummyPartList, &cnt);
3699         if (code) {
3700             PrintDiagnostics("listvol", code);
3701             exit(1);
3702         }
3703     }
3704     for (i = 0; i < cnt; i++) {
3705         if (dummyPartList.partFlags[i] & PARTVALID) {
3706             if (wantExtendedInfo)
3707                 code =
3708                     UV_XListVolumes(aserver, dummyPartList.partId[i], all,
3709                                     &xInfoP, &count);
3710             else
3711                 code =
3712                     UV_ListVolumes(aserver, dummyPartList.partId[i], all,
3713                                    &pntr, &count);
3714             if (code) {
3715                 PrintDiagnostics("listvol", code);
3716                 if (pntr)
3717                     free(pntr);
3718                 exit(1);
3719             }
3720             if (wantExtendedInfo) {
3721                 origxInfoP = xInfoP;
3722                 base = (char *)xInfoP;
3723             } else {
3724                 oldpntr = pntr;
3725                 base = (char *)pntr;
3726             }
3727
3728             if (!fast) {
3729                 if (wantExtendedInfo)
3730                     qsort(base, count, sizeof(volintXInfo), XCompareVolName);
3731                 else
3732                     qsort(base, count, sizeof(volintInfo), CompareVolName);
3733             } else {
3734                 if (wantExtendedInfo)
3735                     qsort(base, count, sizeof(volintXInfo), XCompareVolID);
3736                 else
3737                     qsort(base, count, sizeof(volintInfo), CompareVolID);
3738             }
3739             MapPartIdIntoName(dummyPartList.partId[i], pname);
3740             if (!quiet)
3741                 fprintf(STDOUT,
3742                         "Total number of volumes on server %s partition %s: %lu \n",
3743                         as->parms[0].items->data, pname,
3744                         (unsigned long)count);
3745             if (wantExtendedInfo) {
3746                 if (as->parms[6].items)
3747                     XDisplayVolumes2(aserver, dummyPartList.partId[i], origxInfoP,
3748                                 count, int32list, fast, quiet);
3749                 else
3750                     XDisplayVolumes(aserver, dummyPartList.partId[i], origxInfoP,
3751                                 count, int32list, fast, quiet);
3752                 if (xInfoP)
3753                     free(xInfoP);
3754                 xInfoP = (volintXInfo *) 0;
3755             } else {
3756                 if (as->parms[6].items)
3757                     DisplayVolumes2(aserver, dummyPartList.partId[i], oldpntr,
3758                                     count);
3759                 else
3760                     DisplayVolumes(aserver, dummyPartList.partId[i], oldpntr,
3761                                    count, int32list, fast, quiet);
3762                 if (pntr)
3763                     free(pntr);
3764                 pntr = (volintInfo *) 0;
3765             }
3766         }
3767     }
3768     return 0;
3769 }
3770
3771 static int
3772 SyncVldb(register struct cmd_syndesc *as, void *arock)
3773 {
3774     afs_int32 pnum = 0, code;   /* part name */
3775     char part[10];
3776     int flags = 0;
3777     char *volname = 0;
3778
3779     tserver = 0;
3780     if (as->parms[0].items) {
3781         tserver = GetServer(as->parms[0].items->data);
3782         if (!tserver) {
3783             fprintf(STDERR, "vos: host '%s' not found in host table\n",
3784                     as->parms[0].items->data);
3785             exit(1);
3786         }
3787     }
3788
3789     if (as->parms[1].items) {
3790         pnum = volutil_GetPartitionID(as->parms[1].items->data);
3791         if (pnum < 0) {
3792             fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
3793                     as->parms[1].items->data);
3794             exit(1);
3795         }
3796         if (!IsPartValid(pnum, tserver, &code)) {       /*check for validity of the partition */
3797             if (code)
3798                 PrintError("", code);
3799             else
3800                 fprintf(STDERR,
3801                         "vos: partition %s does not exist on the server\n",
3802                         as->parms[1].items->data);
3803             exit(1);
3804         }
3805         flags = 1;
3806
3807         if (!tserver) {
3808             fprintf(STDERR,
3809                     "The -partition option requires a -server option\n");
3810             exit(1);
3811         }
3812     }
3813
3814     if (as->parms[3].items) {
3815         flags |= 2; /* don't update */
3816     }
3817
3818     if (as->parms[2].items) {
3819         /* Synchronize an individual volume */
3820         volname = as->parms[2].items->data;
3821         code = UV_SyncVolume(tserver, pnum, volname, flags);
3822     } else {
3823         if (!tserver) {
3824             fprintf(STDERR,
3825                     "Without a -volume option, the -server option is required\n");
3826             exit(1);
3827         }
3828         code = UV_SyncVldb(tserver, pnum, flags, 0 /*unused */ );
3829     }
3830
3831     if (code) {
3832         PrintDiagnostics("syncvldb", code);
3833         exit(1);
3834     }
3835
3836     /* Print a summary of what we did */
3837     if (volname)
3838         fprintf(STDOUT, "VLDB volume %s synchronized", volname);
3839     else
3840         fprintf(STDOUT, "VLDB synchronized");
3841     if (tserver) {
3842         fprintf(STDOUT, " with state of server %s", as->parms[0].items->data);
3843     }
3844     if (flags & 1) {
3845         MapPartIdIntoName(pnum, part);
3846         fprintf(STDOUT, " partition %s\n", part);
3847     }
3848     fprintf(STDOUT, "\n");
3849
3850     return 0;
3851 }
3852
3853 static int
3854 SyncServer(register struct cmd_syndesc *as, void *arock)
3855 {
3856     afs_int32 pnum, code;       /* part name */
3857     char part[10];
3858
3859     int flags = 0;
3860
3861     tserver = GetServer(as->parms[0].items->data);
3862     if (!tserver) {
3863         fprintf(STDERR, "vos: host '%s' not found in host table\n",
3864                 as->parms[0].items->data);
3865         exit(1);
3866     }
3867     if (as->parms[1].items) {
3868         pnum = volutil_GetPartitionID(as->parms[1].items->data);
3869         if (pnum < 0) {
3870             fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
3871                     as->parms[1].items->data);
3872             exit(1);
3873         }
3874         if (!IsPartValid(pnum, tserver, &code)) {       /*check for validity of the partition */
3875             if (code)
3876                 PrintError("", code);
3877             else
3878                 fprintf(STDERR,
3879                         "vos : partition %s does not exist on the server\n",
3880                         as->parms[1].items->data);
3881             exit(1);
3882         }
3883         flags = 1;
3884     } else {
3885         pnum = -1;
3886     }
3887
3888     if (as->parms[2].items) {
3889         flags |= 2; /* don't update */
3890     }
3891     code = UV_SyncServer(tserver, pnum, flags, 0 /*unused */ );
3892     if (code) {
3893         PrintDiagnostics("syncserv", code);
3894         exit(1);
3895     }
3896     if (flags & 1) {
3897         MapPartIdIntoName(pnum, part);
3898         fprintf(STDOUT, "Server %s partition %s synchronized with VLDB\n",
3899                 as->parms[0].items->data, part);
3900     } else
3901         fprintf(STDOUT, "Server %s synchronized with VLDB\n",
3902                 as->parms[0].items->data);
3903     return 0;
3904
3905 }
3906
3907 static int
3908 VolumeInfoCmd(char *name)
3909 {
3910     struct nvldbentry entry;
3911     afs_int32 vcode;
3912
3913     /* The vlserver will handle names with the .readonly
3914      * and .backup extension as well as volume ids.
3915      */
3916     vcode = VLDB_GetEntryByName(name, &entry);
3917     if (vcode) {
3918         PrintError("", vcode);
3919         exit(1);
3920     }
3921     MapHostToNetwork(&entry);
3922     EnumerateEntry(&entry);
3923
3924     /* Defect #3027: grubby check to handle locked volume.
3925      * If VLOP_ALLOPERS is set, the entry is locked.
3926      * Leave this routine as is, but put in correct check.
3927      */
3928     PrintLocked(entry.flags);
3929
3930     return 0;
3931 }
3932
3933 static int
3934 VolumeZap(register struct cmd_syndesc *as, void *arock)
3935 {
3936     struct nvldbentry entry;
3937     afs_uint32 volid, zapbackupid = 0, backupid = 0;
3938     afs_int32 code, server, part, err;
3939
3940     if (as->parms[3].items) {
3941         /* force flag is on, use the other version */
3942         return NukeVolume(as);
3943     }
3944
3945     if (as->parms[4].items) {
3946         zapbackupid = 1;
3947     }
3948
3949     volid = vsu_GetVolumeID(as->parms[2].items->data, cstruct, &err);
3950     if (volid == 0) {
3951         if (err)
3952             PrintError("", err);
3953         else
3954             fprintf(STDERR, "vos: can't find volume '%s'\n",
3955                     as->parms[2].items->data);
3956         exit(1);
3957     }
3958     part = volutil_GetPartitionID(as->parms[1].items->data);
3959     if (part < 0) {
3960         fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
3961                 as->parms[1].items->data);
3962         exit(1);
3963     }
3964     server = GetServer(as->parms[0].items->data);
3965     if (!server) {
3966         fprintf(STDERR, "vos: host '%s' not found in host table\n",
3967                 as->parms[0].items->data);
3968         exit(1);
3969     }
3970     if (!IsPartValid(part, server, &code)) {    /*check for validity of the partition */
3971         if (code)
3972             PrintError("", code);
3973         else
3974             fprintf(STDERR,
3975                     "vos : partition %s does not exist on the server\n",
3976                     as->parms[1].items->data);
3977         exit(1);
3978     }
3979     code = VLDB_GetEntryByID(volid, -1, &entry);
3980     if (!code) {
3981         if (volid == entry.volumeId[RWVOL])
3982             backupid = entry.volumeId[BACKVOL];
3983         fprintf(STDERR,
3984                 "Warning: Entry for volume number %lu exists in VLDB (but we're zapping it anyway!)\n",
3985                 (unsigned long)volid);
3986     }
3987     if (zapbackupid) {
3988         volintInfo *pntr = (volintInfo *) 0;
3989
3990         if (!backupid) {
3991             code = UV_ListOneVolume(server, part, volid, &pntr);
3992             if (!code) {
3993                 if (volid == pntr->parentID)
3994                     backupid = pntr->backupID;
3995                 if (pntr)
3996                     free(pntr);
3997             }
3998         }
3999         if (backupid) {
4000             code = UV_VolumeZap(server, part, backupid);
4001             if (code) {
4002                 PrintDiagnostics("zap", code);
4003                 exit(1);
4004             }
4005             fprintf(STDOUT, "Backup Volume %lu deleted\n",
4006                     (unsigned long)backupid);
4007         }
4008     }
4009     code = UV_VolumeZap(server, part, volid);
4010     if (code) {
4011         PrintDiagnostics("zap", code);
4012         exit(1);
4013     }
4014     fprintf(STDOUT, "Volume %lu deleted\n", (unsigned long)volid);
4015
4016     return 0;
4017 }
4018
4019 static int
4020 VolserStatus(register struct cmd_syndesc *as, void *arock)
4021 {
4022     afs_int32 server, code;
4023     transDebugInfo *pntr, *oldpntr;
4024     afs_int32 count;
4025     int i;
4026     char pname[10];
4027     time_t t;
4028
4029     server = GetServer(as->parms[0].items->data);
4030     if (!server) {
4031         fprintf(STDERR, "vos: host '%s' not found in host table\n",
4032                 as->parms[0].items->data);
4033         exit(1);
4034     }
4035     code = UV_VolserStatus(server, &pntr, &count);
4036     if (code) {
4037         PrintDiagnostics("status", code);
4038         exit(1);
4039     }
4040     oldpntr = pntr;
4041     if (count == 0)
4042         fprintf(STDOUT, "No active transactions on %s\n",
4043                 as->parms[0].items->data);
4044     else {
4045         fprintf(STDOUT, "Total transactions: %d\n", count);
4046     }
4047     for (i = 0; i < count; i++) {
4048         /*print out the relevant info */
4049         fprintf(STDOUT, "--------------------------------------\n");
4050         t = pntr->time;
4051         fprintf(STDOUT, "transaction: %lu  created: %s",
4052                 (unsigned long)pntr->tid, ctime(&t));
4053         if (pntr->returnCode) {
4054             fprintf(STDOUT, "returnCode: %lu\n",
4055                     (unsigned long)pntr->returnCode);
4056         }
4057         if (pntr->iflags) {
4058             fprintf(STDOUT, "attachFlags:  ");
4059             switch (pntr->iflags) {
4060             case ITOffline:
4061                 fprintf(STDOUT, "offline ");
4062                 break;
4063             case ITBusy:
4064                 fprintf(STDOUT, "busy ");
4065                 break;
4066             case ITReadOnly:
4067                 fprintf(STDOUT, "readonly ");
4068                 break;
4069             case ITCreate:
4070                 fprintf(STDOUT, "create ");
4071                 break;
4072             case ITCreateVolID:
4073                 fprintf(STDOUT, "create volid ");
4074                 break;
4075             }
4076             fprintf(STDOUT, "\n");
4077         }
4078         if (pntr->vflags) {
4079             fprintf(STDOUT, "volumeStatus: ");
4080             switch (pntr->vflags) {
4081             case VTDeleteOnSalvage:
4082                 fprintf(STDOUT, "deleteOnSalvage ");
4083             case VTOutOfService:
4084                 fprintf(STDOUT, "outOfService ");
4085             case VTDeleted:
4086                 fprintf(STDOUT, "deleted ");
4087             }
4088             fprintf(STDOUT, "\n");
4089         }
4090         if (pntr->tflags) {
4091             fprintf(STDOUT, "transactionFlags: ");
4092             fprintf(STDOUT, "delete\n");
4093         }
4094         MapPartIdIntoName(pntr->partition, pname);
4095         fprintf(STDOUT, "volume: %lu  partition: %s  procedure: %s\n",
4096                 (unsigned long)pntr->volid, pname, pntr->lastProcName);
4097         if (pntr->callValid) {
4098             fprintf(STDOUT,
4099                     "packetRead: %lu  lastReceiveTime: %d  packetSend: %lu  lastSendTime: %d\n",
4100                     (unsigned long)pntr->readNext, pntr->lastReceiveTime,
4101                     (unsigned long)pntr->transmitNext, pntr->lastSendTime);
4102         }
4103         pntr++;
4104         fprintf(STDOUT, "--------------------------------------\n");
4105         fprintf(STDOUT, "\n");
4106     }
4107     if (oldpntr)
4108         free(oldpntr);
4109     return 0;
4110 }
4111
4112 static int
4113 RenameVolume(register struct cmd_syndesc *as, void *arock)
4114 {
4115     afs_int32 code1, code2, code;
4116     struct nvldbentry entry;
4117
4118     code1 = VLDB_GetEntryByName(as->parms[0].items->data, &entry);
4119     if (code1) {
4120         fprintf(STDERR, "vos: Could not find entry for volume %s\n",
4121                 as->parms[0].items->data);
4122         exit(1);
4123     }
4124     code2 = VLDB_GetEntryByName(as->parms[1].items->data, &entry);
4125     if ((!code1) && (!code2)) { /*the newname already exists */
4126         fprintf(STDERR, "vos: volume %s already exists\n",
4127                 as->parms[1].items->data);
4128         exit(1);
4129     }
4130
4131     if (code1 && code2) {
4132         fprintf(STDERR, "vos: Could not find entry for volume %s or %s\n",
4133                 as->parms[0].items->data, as->parms[1].items->data);
4134         exit(1);
4135     }
4136     if (!VolNameOK(as->parms[0].items->data)) {
4137         fprintf(STDERR,
4138                 "Illegal volume name %s, should not end in .readonly or .backup\n",
4139                 as->parms[0].items->data);
4140         exit(1);
4141     }
4142     if (!ISNAMEVALID(as->parms[1].items->data)) {
4143         fprintf(STDERR,
4144                 "vos: the new volume name %s exceeds the size limit of %d\n",
4145                 as->parms[1].items->data, VOLSER_OLDMAXVOLNAME - 10);
4146         exit(1);
4147     }
4148     if (!VolNameOK(as->parms[1].items->data)) {
4149         fprintf(STDERR,
4150                 "Illegal volume name %s, should not end in .readonly or .backup\n",
4151                 as->parms[1].items->data);
4152         exit(1);
4153     }
4154     if (IsNumeric(as->parms[1].items->data)) {
4155         fprintf(STDERR, "Illegal volume name %s, should not be a number\n",
4156                 as->parms[1].items->data);
4157         exit(1);
4158     }
4159     MapHostToNetwork(&entry);
4160     code =
4161         UV_RenameVolume(&entry, as->parms[0].items->data,
4162                         as->parms[1].items->data);
4163     if (code) {
4164         PrintDiagnostics("rename", code);
4165         exit(1);
4166     }
4167     fprintf(STDOUT, "Renamed volume %s to %s\n", as->parms[0].items->data,
4168             as->parms[1].items->data);
4169     return 0;
4170 }
4171
4172 int
4173 GetVolumeInfo(afs_uint32 volid, afs_int32 *server, afs_int32 *part, afs_int32 *voltype, 
4174               struct nvldbentry *rentry)
4175 {
4176     afs_int32 vcode;
4177     int i, index = -1;
4178
4179     vcode = VLDB_GetEntryByID(volid, -1, rentry);
4180     if (vcode) {
4181         fprintf(STDERR,
4182                 "Could not fetch the entry for volume %lu from VLDB \n",
4183                 (unsigned long)volid);
4184         PrintError("", vcode);
4185         return (vcode);
4186     }
4187     MapHostToNetwork(rentry);
4188     if (volid == rentry->volumeId[ROVOL]) {
4189         *voltype = ROVOL;
4190         for (i = 0; i < rentry->nServers; i++) {
4191             if ((index == -1) && (rentry->serverFlags[i] & ITSROVOL)
4192                 && !(rentry->serverFlags[i] & RO_DONTUSE))
4193                 index = i;
4194         }
4195         if (index == -1) {
4196             fprintf(STDERR,
4197                     "RO volume is not found in VLDB entry for volume %lu\n",
4198                     (unsigned long)volid);
4199             return -1;
4200         }
4201
4202         *server = rentry->serverNumber[index];
4203         *part = rentry->serverPartition[index];
4204         return 0;
4205     }
4206
4207     index = Lp_GetRwIndex(rentry);
4208     if (index == -1) {
4209         fprintf(STDERR,
4210                 "RW Volume is not found in VLDB entry for volume %lu\n",
4211                 (unsigned long)volid);
4212         return -1;
4213     }
4214     if (volid == rentry->volumeId[RWVOL]) {
4215         *voltype = RWVOL;
4216         *server = rentry->serverNumber[index];
4217         *part = rentry->serverPartition[index];
4218         return 0;
4219     }
4220     if (volid == rentry->volumeId[BACKVOL]) {
4221         *voltype = BACKVOL;
4222         *server = rentry->serverNumber[index];
4223         *part = rentry->serverPartition[index];
4224         return 0;
4225     }
4226     fprintf(STDERR,
4227             "unexpected volume type for volume %lu\n",
4228             (unsigned long)volid);
4229     return -1;
4230 }
4231
4232 static int
4233 DeleteEntry(register struct cmd_syndesc *as, void *arock)
4234 {
4235     afs_int32 apart = 0;
4236     afs_uint32 avolid;
4237     afs_int32 vcode;
4238     struct VldbListByAttributes attributes;
4239     nbulkentries arrayEntries;
4240     register struct nvldbentry *vllist;
4241     struct cmd_item *itp;
4242     afs_int32 nentries;
4243     int j;
4244     char prefix[VOLSER_MAXVOLNAME + 1];
4245     int seenprefix = 0;
4246     afs_int32 totalBack = 0, totalFail = 0, err;
4247
4248     if (as->parms[0].items) {   /* -id */
4249         if (as->parms[1].items || as->parms[2].items || as->parms[3].items) {
4250             fprintf(STDERR,
4251                     "You cannot use -server, -partition, or -prefix with the -id argument\n");
4252             exit(-2);
4253         }
4254         for (itp = as->parms[0].items; itp; itp = itp->next) {
4255             avolid = vsu_GetVolumeID(itp->data, cstruct, &err);
4256             if (avolid == 0) {
4257                 if (err)
4258                     PrintError("", err);
4259                 else
4260                     fprintf(STDERR, "vos: can't find volume '%s'\n",
4261                             itp->data);
4262                 continue;
4263             }
4264             if (as->parms[4].items) {   /* -noexecute */
4265                 fprintf(STDOUT, "Would have deleted VLDB entry for %s \n",
4266                         itp->data);
4267                 fflush(STDOUT);
4268                 continue;
4269             }
4270             vcode = ubik_VL_DeleteEntry(cstruct, 0, avolid, RWVOL);
4271             if (vcode) {
4272                 fprintf(STDERR, "Could not delete entry for volume %s\n",
4273                         itp->data);
4274                 fprintf(STDERR,
4275                         "You must specify a RW volume name or ID "
4276                         "(the entire VLDB entry will be deleted)\n");
4277                 PrintError("", vcode);
4278                 totalFail++;
4279                 continue;
4280             }
4281             totalBack++;
4282         }
4283         fprintf(STDOUT, "Deleted %d VLDB entries\n", totalBack);
4284         return (totalFail);
4285     }
4286
4287     if (!as->parms[1].items && !as->parms[2].items && !as->parms[3].items) {
4288         fprintf(STDERR, "You must specify an option\n");
4289         exit(-2);
4290     }
4291
4292     /* Zero out search attributes */
4293     memset(&attributes, 0, sizeof(struct VldbListByAttributes));
4294
4295     if (as->parms[1].items) {   /* -prefix */
4296         strncpy(prefix, as->parms[1].items->data, VOLSER_MAXVOLNAME);
4297         seenprefix = 1;
4298         if (!as->parms[2].items && !as->parms[3].items) {       /* a single entry only */
4299             fprintf(STDERR,
4300                     "You must provide -server with the -prefix argument\n");
4301             exit(-2);
4302         }
4303     }
4304
4305     if (as->parms[2].items) {   /* -server */
4306         afs_int32 aserver;
4307         aserver = GetServer(as->parms[2].items->data);
4308         if (aserver == 0) {
4309             fprintf(STDERR, "vos: server '%s' not found in host table\n",
4310                     as->parms[2].items->data);
4311             exit(-1);
4312         }
4313         attributes.server = ntohl(aserver);
4314         attributes.Mask |= VLLIST_SERVER;
4315     }
4316
4317     if (as->parms[3].items) {   /* -partition */
4318         if (!as->parms[2].items) {
4319             fprintf(STDERR,
4320                     "You must provide -server with the -partition argument\n");