c95c4fe2f3d32a0c854c79f4df9134517e2ee20e
[openafs.git] / src / volser / dumpstuff.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 RCSID("$Header$");
14
15 #include <sys/types.h>
16 #include <ctype.h>
17 #include <stdio.h>
18 #include <errno.h>
19 #ifdef AFS_NT40_ENV
20 #include <fcntl.h>
21 #else
22 #include <sys/param.h>
23 #include <sys/file.h>
24 #include <sys/uio.h>
25 #include <netinet/in.h>
26 #include <unistd.h>
27 #endif
28 #ifdef HAVE_STRING_H
29 #include <string.h>
30 #else
31 #ifdef HAVE_STRINGS_H
32 #include <strings.h>
33 #endif
34 #endif
35 #include <sys/stat.h>
36 #include <afs/assert.h>
37 #include <rx/xdr.h>
38 #include <rx/rx.h>
39 #include <afs/afsint.h>
40 #include <afs/nfs.h>
41 #include <afs/errors.h>
42 #include <lock.h>
43 #include <lwp.h>
44 #include <afs/ihandle.h>
45 #include <afs/vnode.h>
46 #include <afs/volume.h>
47 #include <afs/partition.h>
48 #include "dump.h"
49 #include <afs/fssync.h>
50 #include <afs/acl.h>
51 #include "volser.h"
52 #include "volint.h"
53
54 extern int DoLogging;
55
56 /* This iod stuff is a silly little package to emulate the old qi_in stuff, which
57    emulated the stdio stuff.  There is a big assumption here, that the
58    rx_Read will never be called directly, by a routine like readFile, when
59    there is an old character that was pushed back with iod_ungetc.  This
60    is really bletchy, and is here for compatibility only.  Eventually,
61    we should define a volume format that doesn't require
62    the pushing back of characters (i.e. characters should not double both
63    as an end marker and a begin marker) */
64 struct iod {
65     struct rx_call *call;       /* call to which to write, might be an array */
66     int device;                 /* dump device ID for volume */
67     int parentId;               /* dump parent ID for volume */
68     struct DiskPartition *dumpPartition; /* Dump partition. */
69     struct rx_call **calls;      /* array of pointers to calls */
70     int ncalls;                 /* how many calls/codes in array */
71     int *codes;                 /* one return code for each call */
72     char haveOldChar;           /* state for pushing back a character */
73     char oldChar;
74 };
75
76
77 /* Forward Declarations */
78 static int DumpDumpHeader(register struct iod *iodp, register Volume *vp,
79                           afs_int32 fromtime);
80 static int DumpPartial(register struct iod *iodp, register Volume *vp,
81                        afs_int32 fromtime, int dumpAllDirs);
82 static int DumpVnodeIndex(register struct iod *iodp, Volume *vp,
83                           VnodeClass class, afs_int32 fromtime, int forcedump);
84 static int DumpVnode(register struct iod *iodp, struct VnodeDiskObject *v,
85                      int vnodeNumber, int dumpEverything);
86 static int ReadDumpHeader(register struct iod *iodp, struct DumpHeader *hp);
87 static int ReadVnodes(register struct iod *iodp, Volume *vp,
88                       int incremental, afs_int32 *Lbuf, afs_int32 s1,
89                       afs_int32 *Sbuf, afs_int32 s2, afs_int32 delo);
90 static bit32 volser_WriteFile(int vn, struct iod *iodp, FdHandle_t *handleP,
91                               Error *status);
92
93
94 static void iod_Init(register struct iod *iodp, register struct rx_call *call)
95 {
96     iodp->call = call;
97     iodp->haveOldChar = 0;
98     iodp->ncalls = 1;
99     iodp->calls = (struct rx_call **) 0;
100 }
101
102 static void iod_InitMulti(struct iod *iodp, struct rx_call **calls, int ncalls,
103                    int *codes)
104 {
105
106   iodp->calls = calls;
107   iodp->haveOldChar = 0;
108   iodp->ncalls = ncalls;
109   iodp->codes = codes;
110   iodp->call = (struct rx_call *) 0;
111 }
112
113 /* N.B. iod_Read doesn't check for oldchar (see previous comment) */
114 #define iod_Read(iodp, buf, nbytes) rx_Read((iodp)->call, buf, nbytes)
115
116 /* For the single dump case, it's ok to just return the "bytes written"
117  * that rx_Write returns, since all the callers of iod_Write abort when
118  * the returned value is less than they expect.  For the multi dump case,
119  * I don't think we want half the replicas to go bad just because one 
120  * connection timed out, but if they all time out, then we should give up. 
121  */
122 static int iod_Write(struct iod *iodp, char *buf, int nbytes)
123 {
124   int code, i;
125   int one_success = 0;
126
127   assert ((iodp->call && iodp->ncalls == 1 && !iodp->calls) ||
128           (!iodp->call && iodp->ncalls >= 1 && iodp->calls));
129
130   if (iodp->call) {
131     code = rx_Write(iodp->call, buf, nbytes); 
132     return code;     
133   }
134
135   for (i=0; i < iodp->ncalls; i++) {
136     if (iodp->calls[i] && !iodp->codes[i]) {
137       code = rx_Write(iodp->calls[i], buf, nbytes);
138       if (code != nbytes) { /* everything gets merged into a single error */
139         iodp->codes[i] = VOLSERDUMPERROR;  /* but that's exactly what the */
140       }                                    /* standard dump does, anyways */
141       else {
142         one_success = TRUE;
143       }
144     }
145   } /* for all calls */
146
147 if (one_success)
148   return nbytes;
149 else return 0;
150 }
151
152 static void iod_ungetc(struct iod *iodp, int achar)
153 {
154     iodp->oldChar = achar;
155     iodp->haveOldChar = 1;
156 }
157
158 static int iod_getc(register struct iod *iodp)
159 {
160     unsigned char t;
161    
162     if (iodp->haveOldChar) {
163         iodp->haveOldChar = 0;
164         return iodp->oldChar;
165     }
166     if (iod_Read(iodp, &t, 1) == 1) return t;
167     return EOF;
168 }
169
170 static int ReadShort(register struct iod *iodp, register unsigned short *sp)
171 {
172     register b1,b0;
173     b1 = iod_getc(iodp);
174     b0 = iod_getc(iodp);
175     *sp = (b1<<8) | b0;
176     return b0 != EOF;
177 }
178
179 static int ReadInt32(register struct iod *iodp, afs_uint32 *lp)
180 {
181     afs_uint32 register b3,b2,b1,b0;
182     b3 = iod_getc(iodp);
183     b2 = iod_getc(iodp);
184     b1 = iod_getc(iodp);
185     b0 = iod_getc(iodp);
186     *lp = (((((b3<<8)|b2)<<8)|b1)<<8)|b0;
187     return b0 != EOF;
188 }
189
190 static void ReadString(register struct iod *iodp, register char *to,
191                       register int maxa)
192 {
193     register int c;
194     while (maxa--) {
195         if ((*to++ = iod_getc(iodp)) == 0)
196             break;
197     }
198     if (to[-1]) {
199         while ((c = iod_getc(iodp)) && c != EOF);
200         to[-1] = 0;
201     }
202 }
203
204 static void ReadByteString(register struct iod *iodp, register byte *to,
205                    register int size)
206 {
207     while (size--)
208         *to++ = iod_getc(iodp);
209 }
210
211 static int ReadVolumeHeader(register struct iod *iodp, VolumeDiskData *vol)
212 {
213     register tag;
214     afs_uint32 trash;
215     memset(vol, 0, sizeof(*vol));
216     while ((tag = iod_getc(iodp)) > D_MAX && tag != EOF) {
217         switch (tag) {
218             case 'i':
219                 if(!ReadInt32(iodp, &vol->id)) return VOLSERREAD_DUMPERROR;
220                 break;
221             case 'v':
222                 if(!ReadInt32(iodp, &trash)) return VOLSERREAD_DUMPERROR;
223                 break;
224             case 'n':
225                  ReadString(iodp, vol->name, sizeof(vol->name));
226                 /*this means the name of the retsored volume could be possibly different. In conjunction with SAFSVolSignalRestore */
227                 break;
228             case 's':
229                 vol->inService = iod_getc(iodp );
230                 break;
231             case 'b':
232                 vol->blessed = iod_getc(iodp );
233                 break;
234             case 'u':
235                 if(!ReadInt32(iodp, &vol->uniquifier)) return VOLSERREAD_DUMPERROR;
236                 break;
237             case 't':
238                 vol->type = iod_getc(iodp );
239                 break;
240             case 'p':
241                 if(!ReadInt32(iodp, &vol->parentId)) return VOLSERREAD_DUMPERROR;
242                 break;
243             case 'c':
244                 if(!ReadInt32(iodp, &vol->cloneId)) return VOLSERREAD_DUMPERROR;
245                 break;
246             case 'q':
247                 if(!ReadInt32(iodp, (afs_uint32 *)&vol->maxquota)) return VOLSERREAD_DUMPERROR;
248                 break;
249             case 'm':
250                 if(!ReadInt32(iodp, (afs_uint32 *)&vol->minquota)) return VOLSERREAD_DUMPERROR;
251                 break;
252             case 'd':
253                 if(!ReadInt32(iodp, (afs_uint32 *)&vol->diskused)) return VOLSERREAD_DUMPERROR; /* Bogus:  should calculate this */
254                 break;
255             case 'f':
256                 if(!ReadInt32(iodp, (afs_uint32 *)&vol->filecount)) return VOLSERREAD_DUMPERROR;
257                 break;
258             case 'a':
259                 if(!ReadInt32(iodp, &vol->accountNumber)) return VOLSERREAD_DUMPERROR;
260                 break;
261             case 'o':
262                 if(!ReadInt32(iodp, &vol->owner)) return VOLSERREAD_DUMPERROR;
263                 break;
264             case 'C':
265                 if(!ReadInt32(iodp, &vol->creationDate)) return VOLSERREAD_DUMPERROR;
266                 break;
267             case 'A':
268                 if(!ReadInt32(iodp, &vol->accessDate)) return VOLSERREAD_DUMPERROR;
269                 break;
270             case 'U':
271                 if(!ReadInt32(iodp, &vol->updateDate)) return VOLSERREAD_DUMPERROR;
272                 break;
273             case 'E':
274                 if(!ReadInt32(iodp, &vol->expirationDate)) return VOLSERREAD_DUMPERROR;
275                 break;
276             case 'B':
277                 if(!ReadInt32(iodp, &vol->backupDate)) return VOLSERREAD_DUMPERROR;
278                 break;
279             case 'O':
280                 ReadString(iodp, vol->offlineMessage, sizeof(vol->offlineMessage));
281                 break;
282             case 'M':
283                 /*
284                  * Detailed volume statistics are never stored in dumps,
285                  * so we just restore either the null string if this volume
286                  * had already been set to store statistics, or the old motd
287                  * contents otherwise.  It doesn't matter, since this field
288                  * will soon get initialized anyway.
289                  */
290                 ReadString(iodp, (char *)(vol->stat_reads), VMSGSIZE);
291                 break;
292             case 'W': {
293                 unsigned short length;
294                 int i;
295                 afs_uint32 data;
296                 if(!ReadShort(iodp, &length)) return VOLSERREAD_DUMPERROR;
297                 for (i = 0; i<length; i++) {
298                     if(!ReadInt32(iodp, &data)) return VOLSERREAD_DUMPERROR;
299                     if (i < sizeof(vol->weekUse)/sizeof(vol->weekUse[0]))
300                         vol->weekUse[i] = data;
301                 }
302                 break;
303             }
304             case 'D':
305                 if(!ReadInt32(iodp, &vol->dayUseDate)) return VOLSERREAD_DUMPERROR;
306                 break;
307             case 'Z':
308                 if(!ReadInt32(iodp, (afs_uint32 *)&vol->dayUse)) return VOLSERREAD_DUMPERROR;
309                 break;
310         }
311     }
312     iod_ungetc(iodp, tag);
313     return 0;
314 }
315
316 static int DumpTag(register struct iod *iodp, register int tag)
317 {
318     char p;
319     
320     p = tag;
321     return((iod_Write(iodp, &p, 1) == 1)? 0 : VOLSERDUMPERROR);
322     
323 }
324
325 static int DumpByte(register struct iod *iodp, char tag, byte value)
326 {
327     char tbuffer[2];
328     register byte *p = (unsigned char *) tbuffer;
329     *p++ = tag;
330     *p = value;
331     return((iod_Write(iodp, tbuffer, 2) == 2)? 0: VOLSERDUMPERROR);
332 }
333
334 #define putint32(p, v)  *p++ = v>>24, *p++ = v>>16, *p++ = v>>8, *p++ = v
335 #define putshort(p, v) *p++ = v>>8, *p++ = v
336
337 static int DumpDouble(register struct iod *iodp, char tag,
338                       register afs_uint32 value1, register afs_uint32 value2)
339 {
340     char tbuffer[9];
341     register byte *p = (unsigned char *) tbuffer;
342     *p++ = tag;
343     putint32(p, value1);
344     putint32(p, value2);
345     return((iod_Write(iodp, tbuffer, 9) == 9)? 0: VOLSERDUMPERROR);
346 }
347
348 static int DumpInt32(register struct iod *iodp, char tag,
349                      register afs_uint32 value)
350 {
351     char tbuffer[5];
352     register byte *p = (unsigned char *) tbuffer;
353     *p++ = tag;
354     putint32(p, value);
355     return((iod_Write(iodp, tbuffer, 5) == 5)? 0: VOLSERDUMPERROR);
356 }
357
358 static int DumpArrayInt32(register struct iod *iodp, char tag,
359                           register afs_uint32 *array, register int nelem)
360 {
361     char tbuffer[4];
362     register afs_uint32 v ;
363     int code = 0;
364     register byte *p = (unsigned char *) tbuffer;
365     *p++ = tag;
366     putshort(p, nelem);
367     code = iod_Write(iodp, tbuffer, 3);
368     if (code != 3) return VOLSERDUMPERROR;
369     while (nelem--) {
370         p = (unsigned char *) tbuffer;
371         v = *array++;/*this was register */
372         
373         putint32(p, v);
374         code = iod_Write(iodp, tbuffer, 4);
375         if(code != 4) return VOLSERDUMPERROR;
376     }
377     return 0;
378 }
379
380 static int DumpShort(register struct iod *iodp, char tag, unsigned int value)
381 {
382     char tbuffer[3];
383     register byte *p = (unsigned char *) tbuffer;
384     *p++ = tag;
385     *p++ = value>>8;
386     *p = value;
387     return((iod_Write(iodp, tbuffer, 3) == 3)? 0: VOLSERDUMPERROR);
388 }
389
390 static int DumpBool(register struct iod *iodp, char tag, unsigned int value)
391 {
392     char tbuffer[2];
393     register byte *p = (unsigned char *) tbuffer;
394     *p++ = tag;
395     *p = value;
396     return((iod_Write(iodp, tbuffer, 2) == 2)? 0: VOLSERDUMPERROR);
397 }
398
399 static int DumpString(register struct iod *iodp, char tag, register char *s)
400 {
401     register n;
402     int code = 0;
403     code = iod_Write(iodp, &tag, 1);
404     if(code != 1) return VOLSERDUMPERROR;
405     n = strlen(s)+1;
406     code = iod_Write(iodp, s, n);
407     if(code != n) return VOLSERDUMPERROR;
408     return 0;
409 }
410
411 static int DumpByteString(register struct iod *iodp, char tag,
412                           register byte *bs, register int nbytes)
413 {
414     int code = 0;
415
416     code = iod_Write(iodp, &tag, 1);
417     if(code != 1) return VOLSERDUMPERROR;
418     code = iod_Write(iodp, (char *)bs, nbytes);
419     if(code != nbytes) return VOLSERDUMPERROR;
420     return 0;
421 }
422     
423 static int DumpFile(struct iod *iodp, char tag, int vnode, FdHandle_t *handleP)
424 {
425     int   code = 0, lcode = 0, error = 0;
426     afs_int32 pad = 0, offset;
427     int   n, nbytes, howMany, howBig;
428     byte  *p;
429     struct stat status;
430     int size;
431 #ifdef  AFS_AIX_ENV
432 #include <sys/statfs.h>
433     struct statfs tstatfs;
434 #endif
435
436 #ifdef AFS_NT40_ENV
437     howBig = _filelength(handleP->fd_fd);
438     howMany = 4096;
439
440 #else
441     fstat(handleP->fd_fd, &status);
442     howBig = status.st_size;
443
444 #ifdef  AFS_AIX_ENV
445     /* Unfortunately in AIX valuable fields such as st_blksize are 
446      * gone from the stat structure.
447      */
448     fstatfs(handleP->fd_fd, &tstatfs);
449     howMany = tstatfs.f_bsize;
450 #else
451     howMany = status.st_blksize;
452 #endif /* AFS_AIX_ENV */
453 #endif /* AFS_NT40_ENV */
454
455
456     size = FDH_SIZE(handleP);
457     code = DumpInt32(iodp, tag, size);
458     if (code) {
459        return VOLSERDUMPERROR;
460     }
461
462     p = (unsigned char *) malloc(howMany);
463     if (!p) {
464        Log("1 Volser: DumpFile: no memory");
465        return VOLSERDUMPERROR;
466     }
467
468     for (nbytes = size; (nbytes && !error); nbytes -= howMany) {
469         if (nbytes < howMany) 
470            howMany = nbytes;
471
472         /* Read the data - unless we know we can't */
473         n = (lcode ? 0 : FDH_READ(handleP, p, howMany));        
474
475         /* If read any good data and we null padded previously, log the
476          * amount that we had null padded.
477          */
478         if ((n > 0) && pad) {
479            Log("1 Volser: DumpFile: Null padding file %d bytes at offset %u\n",
480                pad, offset);
481            pad = 0;
482         }
483
484         /* If didn't read enough data, null padd the rest of the buffer. This
485          * can happen if, for instance, the media has some bad spots. We don't
486          * want to quit the dump, so we start null padding.
487          */
488         if (n < howMany) {
489            /* Record the read error */
490            if (n < 0) {
491               n = 0;
492               Log("1 Volser: DumpFile: Error %d reading inode %s for vnode %d\n", 
493                   errno, PrintInode(NULL, handleP->fd_ih->ih_ino), vnode);
494            }
495            else if (!pad) {
496               Log("1 Volser: DumpFile: Error reading inode %s for vnode %d\n",
497                   PrintInode(NULL, handleP->fd_ih->ih_ino), vnode);
498            }
499            
500            /* Pad the rest of the buffer with zeros. Remember offset we started 
501             * padding. Keep total tally of padding.
502             */
503            memset(p+n, 0, howMany-n);
504            if (!pad)
505               offset = (howBig - nbytes) + n;
506            pad += (howMany-n);
507            
508            /* Now seek over the data we could not get. An error here means we
509             * can't do the next read.
510             */
511            lcode = FDH_SEEK(handleP, ((size - nbytes) + howMany), SEEK_SET);
512            if (lcode != ((size - nbytes) + howMany)) {
513               if (lcode < 0) {
514                  Log("1 Volser: DumpFile: Error %d seeking in inode %s for vnode %d\n",
515                      errno, PrintInode(NULL, handleP->fd_ih->ih_ino), vnode);
516               }
517               else {
518                  Log("1 Volser: DumpFile: Error seeking in inode %s for vnode %d\n",
519                      PrintInode(NULL, handleP->fd_ih->ih_ino), vnode);
520                  lcode = -1;
521               }
522            }
523            else {
524              lcode = 0;
525            }
526         }
527
528         /* Now write the data out */
529         if (iod_Write(iodp, (char *)p, howMany) != howMany) 
530            error = VOLSERDUMPERROR;
531         IOMGR_Poll();
532     }
533
534     if (pad) {                     /* Any padding we hadn't reported yet */
535        Log("1 Volser: DumpFile: Null padding file: %d bytes at offset %u\n",
536            pad, offset);
537     }
538
539     free(p);
540     return error;
541 }
542
543 static int DumpVolumeHeader(register struct iod *iodp, register Volume *vp)
544 {
545     int code = 0;
546     static char nullString[1] = "";  /*The ``contents'' of motd*/
547
548     if (!code) code = DumpTag(iodp, D_VOLUMEHEADER);
549     if (!code) {code = DumpInt32(iodp, 'i',V_id(vp));}
550     if (!code) code = DumpInt32(iodp, 'v',V_stamp(vp).version);
551     if (!code) code = DumpString(iodp, 'n',V_name(vp));
552     if (!code) code = DumpBool(iodp, 's',V_inService(vp));
553     if (!code) code = DumpBool(iodp, 'b',V_blessed(vp));
554     if (!code) code = DumpInt32(iodp, 'u',V_uniquifier(vp));
555     if (!code) code = DumpByte(iodp, 't',(byte)V_type(vp));
556     if (!code){ code = DumpInt32(iodp, 'p',V_parentId(vp));}
557     if (!code) code = DumpInt32(iodp, 'c',V_cloneId(vp));
558     if (!code) code = DumpInt32(iodp, 'q',V_maxquota(vp));
559     if (!code) code = DumpInt32(iodp, 'm',V_minquota(vp));
560     if (!code) code = DumpInt32(iodp, 'd',V_diskused(vp));
561     if (!code) code = DumpInt32(iodp, 'f',V_filecount(vp));
562     if (!code) code = DumpInt32(iodp, 'a', V_accountNumber(vp));
563     if (!code) code = DumpInt32(iodp, 'o', V_owner(vp));
564     if (!code) code = DumpInt32(iodp, 'C',V_creationDate(vp));  /* Rw volume creation date */
565     if (!code) code = DumpInt32(iodp, 'A',V_accessDate(vp));
566     if (!code) code = DumpInt32(iodp, 'U',V_updateDate(vp));
567     if (!code) code = DumpInt32(iodp, 'E',V_expirationDate(vp));
568     if (!code) code = DumpInt32(iodp, 'B',V_backupDate(vp));            /* Rw volume backup clone date */
569     if (!code) code = DumpString(iodp, 'O',V_offlineMessage(vp));
570     /*
571      * We do NOT dump the detailed volume statistics residing in the old
572      * motd field, since we cannot tell from the info in a dump whether
573      * statistics data has been put there.  Instead, we dump a null string,
574      * just as if that was what the motd contained.
575      */
576     if (!code) code = DumpString(iodp, 'M', nullString);
577     if (!code) code = DumpArrayInt32(iodp, 'W', (afs_uint32 *)V_weekUse(vp), sizeof(V_weekUse(vp))/sizeof(V_weekUse(vp)[0]));
578     if (!code) code = DumpInt32(iodp, 'D', V_dayUseDate(vp));
579     if (!code) code = DumpInt32(iodp, 'Z', V_dayUse(vp));
580     return code;
581 }
582
583 static int DumpEnd(register struct iod *iodp)
584 {
585     return(DumpInt32(iodp, D_DUMPEND, DUMPENDMAGIC));
586 }
587
588 /* Guts of the dump code */
589
590 /* Dump a whole volume */
591 int DumpVolume(register struct rx_call *call, register Volume *vp,
592                       afs_int32 fromtime, int dumpAllDirs)
593 {
594     struct iod iod;
595     int code = 0;
596     register struct iod *iodp = &iod;
597     iod_Init(iodp, call);
598     
599     if (!code) code = DumpDumpHeader(iodp, vp, fromtime);
600     
601     if (!code) code = DumpPartial(iodp, vp, fromtime, dumpAllDirs);
602     
603 /* hack follows.  Errors should be handled quite differently in this version of dump than they used to be.*/
604     if (rx_Error(iodp->call)) {
605         Log("1 Volser: DumpVolume: Rx call failed during dump, error %d\n", rx_Error(iodp->call));
606         return VOLSERDUMPERROR;
607     }
608     if (!code) code = DumpEnd(iodp);
609     
610     return code;
611 }
612
613 /* Dump a volume to multiple places*/
614 int DumpVolMulti(struct rx_call **calls, int ncalls, Volume *vp,
615                  afs_int32 fromtime, int dumpAllDirs, int *codes)
616 {
617     struct iod iod;
618     int code = 0;
619     iod_InitMulti(&iod, calls, ncalls, codes);
620     
621     if (!code) code = DumpDumpHeader(&iod, vp, fromtime);
622     if (!code) code = DumpPartial(&iod, vp, fromtime, dumpAllDirs);
623     if (!code) code = DumpEnd(&iod);
624     return code;
625 }
626
627 /* A partial dump (no dump header) */
628 static int DumpPartial(register struct iod *iodp, register Volume *vp,
629                        afs_int32 fromtime, int dumpAllDirs)
630 {
631     int code = 0;
632     if (!code) code = DumpVolumeHeader(iodp, vp);
633     if (!code) code = DumpVnodeIndex(iodp, vp, vLarge, fromtime, dumpAllDirs);
634     if (!code) code = DumpVnodeIndex(iodp, vp, vSmall, fromtime, 0);
635     return code;
636 }
637
638 static int DumpVnodeIndex(register struct iod *iodp, Volume *vp,
639                           VnodeClass class, afs_int32 fromtime, int forcedump)
640 {
641     register int code = 0;
642     register struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
643     char buf[SIZEOF_LARGEDISKVNODE];
644     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf;
645     StreamHandle_t *file;
646     FdHandle_t *fdP;
647     int size;
648     int flag;
649     register int vnodeIndex, nVnodes;
650
651     fdP = IH_OPEN(vp->vnodeIndex[class].handle);
652     assert(fdP != NULL);
653     file = FDH_FDOPEN(fdP, "r+");
654     assert(file != NULL);
655     size = OS_SIZE(fdP->fd_fd);
656     assert(size != -1);
657     nVnodes = (size / vcp->diskSize) - 1;
658     if (nVnodes > 0) {
659         assert((nVnodes+1)*vcp->diskSize == size)
660         assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0)
661     }
662     else nVnodes = 0;
663     for (vnodeIndex = 0; nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1 && !code;
664       nVnodes--, vnodeIndex++) {
665         flag = forcedump || (vnode->serverModifyTime >= fromtime);
666         /* Note:  the >= test is very important since some old volumes may not have
667            a serverModifyTime.  For an epoch dump, this results in 0>=0 test, which
668            does dump the file! */
669         if (!code) code = DumpVnode(iodp, vnode, bitNumberToVnodeNumber(vnodeIndex, class), flag);
670         if (!flag) IOMGR_Poll(); /* if we dont' xfr data, but scan instead, could lose conn */
671     }
672     STREAM_CLOSE(file);
673     FDH_CLOSE(fdP);
674     return code;
675 }
676
677 static int DumpDumpHeader(register struct iod *iodp, register Volume *vp,
678                           afs_int32 fromtime)
679 {
680     int code = 0;
681     int UseLatestReadOnlyClone = 1;
682     afs_int32 dumpTimes[2];
683     iodp->device = vp->device;
684     iodp->parentId = V_parentId(vp);
685     iodp->dumpPartition = vp->partition;
686     if (!code) code = DumpDouble(iodp, D_DUMPHEADER, DUMPBEGINMAGIC, DUMPVERSION);
687     if (!code) code = DumpInt32(iodp, 'v', UseLatestReadOnlyClone? V_id(vp): V_parentId(vp));
688     if (!code) code = DumpString(iodp, 'n',V_name(vp));
689     dumpTimes[0] = fromtime;
690     dumpTimes[1] = V_backupDate(vp);    /* Until the time the clone was made */
691     if (!code) code = DumpArrayInt32(iodp, 't', (afs_uint32 *)dumpTimes, 2);
692     return code;
693 }
694
695 static int DumpVnode(register struct iod *iodp, struct VnodeDiskObject *v,
696                      int vnodeNumber, int dumpEverything)
697 {
698     int code = 0;
699     IHandle_t *ihP;
700     FdHandle_t *fdP;
701
702     if (!v || v->type == vNull)
703         return code;
704     if (!code) code = DumpDouble(iodp, D_VNODE, vnodeNumber, v->uniquifier);
705     if (!dumpEverything)
706         return code;
707     if (!code) code = DumpByte(iodp, 't',(byte)v->type);
708     if (!code) code = DumpShort(iodp, 'l', v->linkCount); /* May not need this */
709     if (!code) code = DumpInt32(iodp, 'v', v->dataVersion);
710     if (!code) code = DumpInt32(iodp, 'm', v->unixModifyTime);
711     if (!code) code = DumpInt32(iodp, 'a', v->author);
712     if (!code) code = DumpInt32(iodp, 'o', v->owner);
713     if (!code && v->group) code = DumpInt32(iodp, 'g', v->group);       /* default group is 0 */
714     if (!code) code = DumpShort(iodp, 'b', v->modeBits);
715     if (!code) code = DumpInt32(iodp, 'p', v->parent);
716     if (!code) code = DumpInt32(iodp, 's', v->serverModifyTime);
717     if (v->type == vDirectory) {
718         acl_HtonACL(VVnodeDiskACL(v));
719         if (!code) code = DumpByteString(iodp, 'A', (byte *) VVnodeDiskACL(v), VAclDiskSize(v));
720     }
721     if (VNDISK_GET_INO(v)) {
722         IH_INIT(ihP, iodp->device, iodp->parentId, VNDISK_GET_INO(v));
723         fdP = IH_OPEN(ihP);
724         if (fdP == NULL) {
725             Log("1 Volser: DumpVnode: dump: Unable to open inode %d for vnode %d; not dumped, error %d\n",
726                 VNDISK_GET_INO(v), vnodeNumber, errno);
727             IH_RELEASE(ihP);
728             return VOLSERREAD_DUMPERROR;
729         }
730         code = DumpFile(iodp, 'f', vnodeNumber, fdP);
731         FDH_CLOSE(fdP);
732         IH_RELEASE(ihP);
733     }
734     return code;
735 }
736
737
738 int ProcessIndex(Volume *vp, VnodeClass class, afs_int32 **Bufp, int *sizep,
739                  int del)
740 {
741     int i, nVnodes, offset, code, index=0;
742     afs_int32 *Buf;
743     int cnt=0;  
744     int size;
745     StreamHandle_t *afile;
746     FdHandle_t *fdP;
747     struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
748     char buf[SIZEOF_LARGEDISKVNODE], zero[SIZEOF_LARGEDISKVNODE];
749     register struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf;
750     
751     memset(zero, 0, sizeof(zero));      /* zero out our proto-vnode */
752     fdP = IH_OPEN(vp->vnodeIndex[class].handle);
753     if (fdP == NULL)
754         return -1;
755     afile = FDH_FDOPEN(fdP, "r+");
756     if (del) {
757         int cnt1=0;
758         Buf = *Bufp;
759         for (i = 0; i<*sizep; i++) {
760             if (Buf[i]) {
761                 cnt++;
762                 STREAM_SEEK(afile, Buf[i], 0);    
763                 code = STREAM_READ(vnode, vcp->diskSize, 1, afile);
764                 if (code == 1) {
765                     if (vnode->type != vNull && VNDISK_GET_INO(vnode)) {
766                         cnt1++;
767                         if (DoLogging) {
768                            Log("RestoreVolume %d Cleanup: Removing old vnode=%d inode=%d size=%d\n", 
769                                V_id(vp), bitNumberToVnodeNumber(i,class),
770                                VNDISK_GET_INO(vnode), vnode->length);
771                         }
772                         IH_DEC(V_linkHandle(vp), VNDISK_GET_INO(vnode),
773                              V_parentId(vp));
774                         DOPOLL;
775                     }
776                     STREAM_SEEK(afile, Buf[i], 0);
777                     (void ) STREAM_WRITE(zero, vcp->diskSize, 1, afile);        /* Zero it out */
778                 }
779                 Buf[i] = 0;
780             }
781         }
782         if (DoLogging) {
783            Log("RestoreVolume Cleanup: Removed %d inodes for volume %d\n",
784                cnt1, V_id(vp));
785         }
786         STREAM_FLUSH(afile);            /* ensure 0s are on the disk */
787         OS_SYNC(afile->str_fd);
788     } else {
789         size = OS_SIZE(fdP->fd_fd);
790         assert(size != -1)
791         nVnodes = (size <= vcp->diskSize ? 0 :
792                    size-vcp->diskSize) >> vcp->logSize;
793         if (nVnodes > 0) {
794             Buf = (afs_int32 *) malloc(nVnodes * sizeof(afs_int32));
795             if (Buf == NULL) return 1;
796             memset((char *)Buf, 0, nVnodes * sizeof(afs_int32));
797             STREAM_SEEK(afile, offset = vcp->diskSize, 0);
798             while (1) {
799                 code = STREAM_READ(vnode, vcp->diskSize, 1, afile);
800                 if (code != 1) {
801                     break;
802                 }
803                 if (vnode->type != vNull && VNDISK_GET_INO(vnode)) {
804                     Buf[(offset >> vcp->logSize)-1] = offset;
805                     cnt++;
806                 }
807                 offset += vcp->diskSize;
808             }
809             *Bufp = Buf;
810             *sizep = nVnodes;
811         }
812     }
813     STREAM_CLOSE(afile);
814     FDH_CLOSE(fdP);
815     return 0;
816 }
817
818
819 int RestoreVolume(register struct rx_call *call, Volume *avp,
820                   int incremental, struct restoreCookie *cookie)
821 {
822     VolumeDiskData vol;
823     struct DumpHeader header;
824     afs_uint32 endMagic;
825     Error error = 0, vupdate;
826     register Volume *vp;
827     struct iod iod;
828     register struct iod *iodp = &iod;
829     afs_int32 *b1=0, *b2=0;
830     int s1=0, s2=0, delo=0, tdelo;
831     int tag;
832
833     iod_Init(iodp, call);
834     
835     vp = avp;
836     if (!ReadDumpHeader(iodp, &header)){
837         Log("1 Volser: RestoreVolume: Error reading header file for dump; aborted\n");
838         return VOLSERREAD_DUMPERROR;
839     }
840     if (iod_getc(iodp) != D_VOLUMEHEADER){
841         Log("1 Volser: RestoreVolume: Volume header missing from dump; not restored\n");
842         return VOLSERREAD_DUMPERROR;
843     }
844     if(ReadVolumeHeader(iodp, &vol) == VOLSERREAD_DUMPERROR) return VOLSERREAD_DUMPERROR;
845
846     delo = ProcessIndex(vp, vLarge, &b1, &s1, 0);
847     if (!delo) delo = ProcessIndex(vp, vSmall, &b2, &s2, 0);
848     if (delo) {
849        if (b1) free((char *)b1);
850        if (b2) free((char *)b2);
851        b1 = b2 = 0;
852     }
853
854     strncpy(vol.name,cookie->name,VOLSER_OLDMAXVOLNAME);
855     vol.type = cookie->type;
856     vol.cloneId = cookie->clone;
857     vol.parentId = cookie->parent;
858     
859
860     tdelo = delo;
861     while (1) {
862        if (ReadVnodes(iodp, vp, 0, b1, s1, b2, s2, tdelo)) {
863           error =  VOLSERREAD_DUMPERROR;
864           goto clean;
865        }
866        tag = iod_getc(iodp);
867        if (tag != D_VOLUMEHEADER)
868           break;
869        if (ReadVolumeHeader(iodp, &vol) == VOLSERREAD_DUMPERROR) {
870           error = VOLSERREAD_DUMPERROR;
871           goto out;
872        }
873        tdelo = -1;
874     }
875     if (tag != D_DUMPEND || !ReadInt32(iodp, &endMagic) || endMagic != DUMPENDMAGIC){
876        Log("1 Volser: RestoreVolume: End of dump not found; restore aborted\n");
877        error =  VOLSERREAD_DUMPERROR;
878        goto clean;
879     }
880
881
882     if (iod_getc(iodp) != EOF) {
883         Log("1 Volser: RestoreVolume: Unrecognized postamble in dump; restore aborted\n");
884         error = VOLSERREAD_DUMPERROR;
885         goto clean;
886     }
887
888     if (!delo) {
889         ProcessIndex(vp, vLarge, &b1, &s1, 1);
890         ProcessIndex(vp, vSmall, &b2, &s2, 1);
891     }
892
893  clean:
894     ClearVolumeStats(&vol);
895     CopyVolumeHeader(&vol, &V_disk(vp));
896     V_destroyMe(vp) = 0;
897     VUpdateVolume(&vupdate, vp);
898     if (vupdate) {
899         Log("1 Volser: RestoreVolume: Unable to rewrite volume header; restore aborted\n");
900         error = VOLSERREAD_DUMPERROR;
901         goto out;
902     }
903  out:
904     /* Free the malloced space above */
905     if (b1) free((char *)b1);
906     if (b2) free((char *)b2);
907     return error;
908 }
909
910 static int ReadVnodes(register struct iod *iodp, Volume *vp,
911                       int incremental, afs_int32 *Lbuf, afs_int32 s1,
912                       afs_int32 *Sbuf, afs_int32 s2, afs_int32 delo)
913 {
914     afs_int32 vnodeNumber;
915     char buf[SIZEOF_LARGEDISKVNODE];
916     register tag;
917     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf;
918     struct VnodeDiskObject oldvnode;
919     int idx;
920     VnodeClass class;
921     struct VnodeClassInfo *vcp;
922     IHandle_t *tmpH;
923     FdHandle_t *fdP;
924     Inode       nearInode;
925
926     tag = iod_getc(iodp);
927     V_pref(vp, nearInode);
928     while (tag == D_VNODE) {
929         int haveStuff = 0;
930         memset(buf, 0, sizeof (buf));
931         if (!ReadInt32(iodp, (afs_uint32 *)&vnodeNumber))
932             break;
933
934         ReadInt32(iodp, &vnode->uniquifier);
935         while ((tag = iod_getc(iodp)) > D_MAX && tag != EOF) {
936             haveStuff = 1;
937             switch (tag) {
938                 case 't':
939                     vnode->type = (VnodeType) iod_getc(iodp);
940                     break;
941                 case 'l':
942                     {
943                         unsigned short tlc;
944                         ReadShort(iodp, &tlc);
945                         vnode->linkCount = (signed int)tlc;
946                     }
947                     break;
948                 case 'v':
949                     ReadInt32(iodp, &vnode->dataVersion);
950                     break;
951                 case 'm':
952                     ReadInt32(iodp, &vnode->unixModifyTime);
953                     break;
954                 case 's':
955                     ReadInt32(iodp, &vnode->serverModifyTime);
956                     break;
957                 case 'a':
958                     ReadInt32(iodp, &vnode->author);
959                     break;
960                 case 'o':
961                     ReadInt32(iodp, &vnode->owner);
962                     break;
963                 case 'g':
964                     ReadInt32(iodp, (afs_uint32 *)&vnode->group);
965                     break;
966                 case 'b': {
967                     unsigned short modeBits;
968                     ReadShort(iodp, &modeBits);
969                     vnode->modeBits = (unsigned int)modeBits;
970                     break;
971                 }
972                 case 'p':
973                     ReadInt32(iodp, &vnode->parent);
974                     break;
975                 case 'A':
976                     ReadByteString(iodp, (byte *)VVnodeDiskACL(vnode),
977                                    VAclDiskSize(vnode));
978                     acl_NtohACL(VVnodeDiskACL(vnode));
979                     break;
980                 case 'f': {
981                     Inode ino;
982                     Error error;
983
984                     ino = IH_CREATE(V_linkHandle(vp),
985                                     V_device(vp),
986                                     VPartitionPath(V_partition(vp)),
987                                     nearInode, V_parentId(vp), vnodeNumber,
988                                     vnode->uniquifier,
989                                     vnode->dataVersion);
990                     if (!VALID_INO(ino)) {
991                         perror("unable to allocate inode");
992                         Log("1 Volser: ReadVnodes: Restore aborted\n");
993                         return VOLSERREAD_DUMPERROR;
994                     }
995                     nearInode = ino;
996                     VNDISK_SET_INO(vnode, ino);
997                     IH_INIT(tmpH, vp->device, V_parentId(vp), ino);
998                     fdP = IH_OPEN(tmpH);
999                     if (fdP == NULL) {
1000                         IH_RELEASE(tmpH);
1001                         return VOLSERREAD_DUMPERROR;
1002                     }
1003                     vnode->length = volser_WriteFile(vnodeNumber, iodp, fdP,
1004                                                      &error);
1005                     FDH_REALLYCLOSE(fdP);
1006                     IH_RELEASE(tmpH);
1007                     if (error) {
1008                         Log("1 Volser: ReadVnodes: IDEC inode %d\n", ino);
1009                         IH_DEC(V_linkHandle(vp), ino, V_parentId(vp));
1010                         return VOLSERREAD_DUMPERROR;
1011                     }
1012                     break;
1013                 }
1014             }
1015         }
1016
1017         class = vnodeIdToClass(vnodeNumber);
1018         vcp   = &VnodeClassInfo[class];
1019
1020         /* Mark this vnode as in this dump - so we don't delete it later */
1021         if (!delo) {
1022            idx = (vnodeIndexOffset(vcp,vnodeNumber) >> vcp->logSize) - 1;
1023            if (class == vLarge) {
1024               if (Lbuf && (idx < s1)) Lbuf[idx] = 0;
1025            } else {
1026               if (Sbuf && (idx < s2)) Sbuf[idx] = 0;
1027            }
1028         }
1029
1030         if (haveStuff) {
1031             FdHandle_t *fdP = IH_OPEN(vp->vnodeIndex[class].handle);
1032             if (fdP == NULL) {
1033                 Log("1 Volser: ReadVnodes: Error opening vnode index; restore aborted\n");
1034                 return VOLSERREAD_DUMPERROR;
1035             }
1036             if (FDH_SEEK(fdP, vnodeIndexOffset(vcp, vnodeNumber), SEEK_SET) < 0) {
1037                 Log("1 Volser: ReadVnodes: Error seeking into vnode index; restore aborted\n");
1038                 FDH_REALLYCLOSE(fdP);
1039                 return VOLSERREAD_DUMPERROR;
1040             }
1041             if (FDH_READ(fdP, &oldvnode, sizeof(oldvnode)) == sizeof(oldvnode)) {
1042                 if (oldvnode.type != vNull && VNDISK_GET_INO(&oldvnode)) {
1043                     IH_DEC(V_linkHandle(vp), VNDISK_GET_INO(&oldvnode),
1044                          V_parentId(vp));
1045                 }
1046             }
1047             vnode->vnodeMagic = vcp->magic;
1048             if (FDH_SEEK(fdP, vnodeIndexOffset(vcp, vnodeNumber), SEEK_SET) < 0) {
1049                 Log("1 Volser: ReadVnodes: Error seeking into vnode index; restore aborted\n");
1050                 FDH_REALLYCLOSE(fdP);
1051                 return VOLSERREAD_DUMPERROR;
1052             }
1053             if (FDH_WRITE(fdP, vnode, vcp->diskSize) != vcp->diskSize) {
1054                 Log("1 Volser: ReadVnodes: Error writing vnode index; restore aborted\n");
1055                 FDH_REALLYCLOSE(fdP);
1056                 return VOLSERREAD_DUMPERROR;
1057             }
1058             FDH_CLOSE(fdP);
1059         }
1060     }
1061     iod_ungetc(iodp, tag);
1062
1063
1064     return 0;
1065 }
1066
1067
1068 /* called with disk file only.  Note that we don't have to worry about rx_Read
1069  * needing to read an ungetc'd character, since the ReadInt32 will have read
1070  * it instead.
1071  */
1072 static bit32 volser_WriteFile(int vn, struct iod *iodp, FdHandle_t *handleP,
1073                               Error *status)
1074 {
1075     afs_int32 code;
1076     afs_uint32 filesize;
1077     bit32 written=0;
1078     register afs_uint32 size = 8192;
1079     register afs_uint32 nbytes;
1080     unsigned char *p;
1081
1082
1083     *status = 0;
1084     if (!ReadInt32(iodp, &filesize)) {
1085         *status = 1;
1086         return(0);
1087     }
1088     p = (unsigned char *) malloc(size);
1089     if (p == NULL) {
1090         *status = 2;
1091         return(0);
1092     }
1093     for (nbytes = filesize; nbytes; nbytes -= size) {
1094         if (nbytes < size)
1095             size = nbytes;
1096         
1097         if ((code = iod_Read(iodp, p, size)) != size) {
1098             Log("1 Volser: WriteFile: Error reading dump file %d size=%d nbytes=%d (%d of %d); restore aborted\n", vn, filesize, nbytes, code, size);
1099             *status = 3;
1100             break;
1101         }
1102         code = FDH_WRITE(handleP, p, size);
1103         if (code > 0) written += code;
1104         if (code != size) {
1105             Log("1 Volser: WriteFile: Error creating file in volume; restore aborted\n");
1106             *status = 4;
1107             break;
1108         }
1109     }
1110     free(p);
1111     return(written);
1112 }
1113
1114 static int ReadDumpHeader(register struct iod *iodp, struct DumpHeader *hp)
1115 {
1116     register tag;
1117     afs_uint32 beginMagic;
1118     if (iod_getc(iodp) != D_DUMPHEADER || !ReadInt32(iodp, &beginMagic)
1119        || !ReadInt32(iodp, (afs_uint32 *)&hp->version)
1120        || beginMagic != DUMPBEGINMAGIC
1121        ) return 0;
1122     hp->volumeId = 0;
1123     hp->nDumpTimes = 0;
1124     while ((tag = iod_getc(iodp)) > D_MAX) {
1125         unsigned short arrayLength;
1126         register int i;
1127         switch(tag) {
1128             case 'v':
1129                 if (!ReadInt32(iodp, &hp->volumeId))
1130                     return 0;
1131                 break;
1132             case 'n':
1133                 ReadString(iodp, hp->volumeName, sizeof(hp->volumeName));
1134                 break;
1135             case 't':
1136                 if (!ReadShort(iodp, &arrayLength))
1137                     return 0;
1138                 hp->nDumpTimes = (arrayLength >> 1);
1139                 for (i = 0; i<hp->nDumpTimes; i++)
1140                     if (!ReadInt32(iodp, (afs_uint32 *)&hp->dumpTimes[i].from)
1141                         || !ReadInt32(iodp, (afs_uint32 *)&hp->dumpTimes[i].to))
1142                         return 0;
1143                 break;
1144         }
1145     }
1146     if (!hp->volumeId || !hp->nDumpTimes) {
1147         return 0;
1148     }
1149     iod_ungetc(iodp, tag);
1150     return 1;
1151 }