DEVEL15-vol-split-nearino-20090324
[openafs.git] / src / volser / vol_split.c
1 /*
2  * Copyright (c) 2007, Hartmut Reuter,
3  * RZG, Max-Planck-Institut f. Plasmaphysik.
4  * All Rights Reserved.
5  *
6  */
7
8 #include <afsconfig.h>
9 #include <afs/param.h>
10
11 #include <sys/types.h>
12 #include <stdio.h>
13 #ifdef AFS_PTHREAD_ENV
14 #include <assert.h>
15 #else /* AFS_PTHREAD_ENV */
16 #include <afs/assert.h>
17 #endif /* AFS_PTHREAD_ENV */
18 #ifdef AFS_NT40_ENV
19 #include <fcntl.h>
20 #include <windows.h>
21 #include <winbase.h>
22 #include <io.h>
23 #include <time.h>
24 #else
25 #include <sys/file.h>
26 #include <sys/time.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 <errno.h>
36 #include <sys/stat.h>
37
38 #include <rx/xdr.h>
39 #include <afs/afsint.h>
40 #include "nfs.h"
41 #include "lwp.h"
42 #include "lock.h"
43 #include <afs/afssyscalls.h>
44 #include "ihandle.h"
45 #include "vnode.h"
46 #include "volume.h"
47 #include "partition.h"
48 #include "viceinode.h"
49 #include "vol.h"
50 #ifdef AFS_RXOSD_SUPPORT
51 #include "rxosd.h"
52 #include "vol_osd.h"
53 #include "../vol/vol_osd_prototypes.h"
54 #endif
55
56 #define NEEDED  1
57 #define PARENT  2
58 #define CHANGEPARENT 4
59
60 #define NAMEI_VNODEMASK    0x03ffffff
61 #define NAMEI_TAGMASK      0x7
62 #define NAMEI_TAGSHIFT     26
63 #define NAMEI_UNIQMASK     0xffffffff
64 #define NAMEI_UNIQSHIFT    32
65
66 struct VnodeExtract {
67     afs_uint32 vN;
68     afs_uint32 parent;
69     afs_uint32 flag;
70 };
71
72 struct Msg {
73     struct rx_call * call;
74     int verbose;
75     char line[1024];
76 };
77
78 afs_int32 ExtractVnodes(struct Msg *m, Volume *vol, afs_int32 class, 
79                         struct VnodeExtract **list,
80                         afs_int32 *length, afs_uint32 where, 
81                         struct VnodeDiskObject *vd,
82                         afs_int32 *parent, struct VnodeDiskObject *parentvd)
83 {
84     afs_int32 code = 0;
85     char buf[SIZEOF_LARGEDISKVNODE];
86     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)&buf;
87     FdHandle_t *fdP = 0;
88     StreamHandle_t *stream = 0;
89     struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
90     struct VnodeExtract *e;
91     afs_uint32 size;
92     afs_uint32 offset;
93     afs_uint32 vN; 
94
95     *length = 0;
96     if (parent)
97         *parent = 0;
98
99     fdP = IH_OPEN(vol->vnodeIndex[class].handle);
100     if (!fdP) {
101         sprintf(m->line, "Couldn't open %s Index of volume %u\n", 
102                 class ? "small":"large", V_id(vol));
103         rx_Write(m->call, m->line, strlen(m->line));
104         code = EIO;
105         goto Bad_Extract;
106     }
107     size = FDH_SIZE(fdP);
108     *list = (struct VnodeExtract *) malloc(size / vcp->diskSize
109                                         * sizeof(struct VnodeExtract));
110     if (!(*list)) {
111         code = ENOMEM;
112         goto Bad_Extract;
113     }
114     memset(*list, 0, size / vcp->diskSize * sizeof(struct VnodeExtract));
115     stream = FDH_FDOPEN(fdP, "r");
116     if (!stream) {
117         sprintf(m->line, "Couldn't stream open %s Index of volume %u\n", 
118                 class ? "small":"large", V_id(vol));
119         rx_Write(m->call, m->line, strlen(m->line));
120         return EIO;
121         goto Bad_Extract;
122     }
123     code = STREAM_SEEK(stream, vcp->diskSize, 0);
124     if (code)
125         goto Bad_Extract;
126
127     offset = vcp->diskSize;
128     e = *list;
129     while (!STREAM_EOF(stream)) {
130         afs_int32 vN = (offset >> (vcp->logSize -1)) - 1 + class;
131         if (STREAM_READ(vnode, vcp->diskSize, 1, stream) == 1) {
132             if (vnode->type != vNull) {
133                 e->vN = vN;
134                 e->parent = vnode->parent;
135                 if (vN == where && class == vLarge) {
136                     memcpy(vd, vnode, vcp->diskSize);
137                     *parent = vnode->parent;
138                 }
139                 e++;
140             }
141             offset += vcp->diskSize;
142         }
143     }
144     *length = (e - *list);
145     if (class == vLarge) {
146         if (*parent) {
147             offset = (*parent + 1 - class) << (vcp->logSize -1);
148             code = STREAM_SEEK(stream, offset, 0);
149             if (STREAM_READ(vnode, vcp->diskSize, 1, stream) == 1) 
150                 memcpy(parentvd, vnode, vcp->diskSize);
151             else
152                 code = EIO;
153         } else {
154             sprintf(m->line, "SplitVolume: extract didn't see directory %u\n", where);
155             rx_Write(m->call, m->line, strlen(m->line));
156             code = ENOENT;
157         }
158     }
159     if (m->verbose) {
160         sprintf(m->line, "Volume %u has %u %s vnodes in volume %uu\n", 
161                         V_parentId(vol), *length, class? "small":"large");
162         rx_Write(m->call, m->line, strlen(m->line));
163     }
164     
165 Bad_Extract:
166     if (stream)
167         STREAM_CLOSE(stream);
168     if (fdP)
169         FDH_CLOSE(fdP);
170     if (code) {
171         free(*list);
172         *list = 0;
173     }
174     return code;
175 }
176
177 afs_int32 FindVnodes(struct Msg *m, afs_uint32 where, 
178                         struct VnodeExtract *list, afs_int32 length, 
179                         struct VnodeExtract *dlist, afs_int32 dlength, 
180                         afs_int32 *needed, afs_int32 class)
181 {
182     afs_int32 i, j, found = 0;
183     afs_int32 parent = 0;
184
185     *needed = 0;
186     for (i=0; i<length; i++) {
187         if (list[i].vN == where) {        /* dir to be replaced by mount point */
188             list[i].flag |= NEEDED;
189             parent = list[i].parent;
190             found = 1;
191             (*needed)++;
192         }
193         if (list[i].parent == where) {          /* all 1st generation children */
194             list[i].flag |= (NEEDED + CHANGEPARENT);
195             (*needed)++;
196         }
197     } 
198     if (list[0].vN & 1) {               /* only for directories */
199         if (!found) {
200             sprintf(m->line, 
201                 "SplitVolume: directory %u where to start new volume not found\n",
202                  where);
203             rx_Write(m->call, m->line, strlen(m->line));
204             return ENOENT;
205         }
206         found = 0;
207         for (i=0; i<length; i++) { 
208             if (list[i].vN == parent) { /* dir where to create mount point */
209                 list[i].flag |= PARENT;
210                 found = 1;
211                 break;
212             }
213         }
214         if (!found) {
215             sprintf(m->line, "SplitVolume: parent directory %u not found\n", 
216                         parent);
217             rx_Write(m->call, m->line, strlen(m->line));
218             return ENOENT;
219         }
220     }
221     found = 1;
222     while (found) {
223         found = 0;
224         for (i=0; i<dlength; i++) {
225             if (!(dlist[i].flag & NEEDED)) /* dirs to remain in old volume */
226                 continue;
227             for (j=0; j<length; j++) {
228                 if (list[j].parent == dlist[i].vN && !(list[j].flag & NEEDED)) {
229                     list[j].flag |= NEEDED;
230                     (*needed)++;
231                     found = 1;
232                 }
233             }
234         }
235     }
236     if (m->verbose) {
237         sprintf(m->line, "%u %s vnodes will go into the new volume\n", 
238                         *needed, class ? "small" : "large");
239         rx_Write(m->call, m->line, strlen(m->line));
240     }
241     return 0;
242 }
243     
244 afs_int32 copyDir(struct Msg *m, IHandle_t *inh, IHandle_t *outh)
245 {
246     afs_int32 code;
247     FdHandle_t *infdP, *outfdP;
248     char *tbuf;
249     afs_size_t size;
250
251     infdP = IH_OPEN(inh);
252     if (!infdP) {
253         sprintf(m->line, "Couldn't open input directory %u.%u.%u\n", 
254                     infdP->fd_ih->ih_vid,
255                     (afs_uint32)(infdP->fd_ih->ih_ino & NAMEI_VNODEMASK),
256                     (afs_uint32)(infdP->fd_ih->ih_ino >> NAMEI_UNIQSHIFT));
257         rx_Write(m->call, m->line, strlen(m->line));
258         return EIO;
259     }
260     outfdP = IH_OPEN(outh);
261     if (!outfdP) {
262         sprintf(m->line, "Couldn't open output directory %u.%u.%u\n", 
263                     outfdP->fd_ih->ih_vid,
264                     (afs_uint32)(outfdP->fd_ih->ih_ino & NAMEI_VNODEMASK),
265                     (afs_uint32)(outfdP->fd_ih->ih_ino >> NAMEI_UNIQSHIFT));
266         rx_Write(m->call, m->line, strlen(m->line));
267         FDH_REALLYCLOSE(infdP);
268         return EIO;
269     }
270     tbuf = malloc(2048);
271     FDH_SEEK(infdP, 0, 0);
272     FDH_SEEK(outfdP, 0, 0);
273     size = FDH_SIZE(infdP);
274     while (size) {
275         afs_int32 tlen;
276         tlen = size > 2048 ? 2048 : size;
277         if (FDH_READ(infdP, tbuf, tlen) != tlen) {
278             sprintf(m->line, "Couldn't read directory %u.%u.%u\n", 
279                     infdP->fd_ih->ih_vid,
280                     (afs_uint32)(infdP->fd_ih->ih_ino & NAMEI_VNODEMASK),
281                     (afs_uint32)(infdP->fd_ih->ih_ino >> NAMEI_UNIQSHIFT));
282             rx_Write(m->call, m->line, strlen(m->line));
283             FDH_REALLYCLOSE(infdP);
284             FDH_REALLYCLOSE(outfdP);
285             free(tbuf);
286             return EIO;
287         }
288         if (FDH_WRITE(outfdP, tbuf, tlen) != tlen) {
289             sprintf(m->line, "Couldn't write directory %u.%u.%u\n", 
290                     outfdP->fd_ih->ih_vid,
291                     (afs_uint32)(outfdP->fd_ih->ih_ino & NAMEI_VNODEMASK),
292                     (afs_uint32)(outfdP->fd_ih->ih_ino >> NAMEI_UNIQSHIFT));
293             rx_Write(m->call, m->line, strlen(m->line));
294             FDH_REALLYCLOSE(infdP);
295             FDH_REALLYCLOSE(outfdP);
296             free(tbuf);
297             return EIO;
298         }
299         size -= tlen;
300     }
301     free(tbuf);
302     FDH_CLOSE(outfdP);
303     FDH_REALLYCLOSE(infdP);
304     return 0;
305 }
306
307 afs_int32 copyVnodes(struct Msg *m, Volume *vol, Volume *newvol, 
308                         afs_int32 class, 
309                         struct VnodeExtract *list, afs_int32 length,
310                         afs_int32 where, afs_uint64 *blocks,
311                         struct VnodeDiskObject *parVnode) 
312 {
313     afs_int32 i, code = 0;
314     char buf[SIZEOF_LARGEDISKVNODE];
315     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)&buf;
316     FdHandle_t *fdP = 0;
317     FdHandle_t *newfdP = 0;
318     StreamHandle_t *newstream = 0;
319     struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
320     struct VnodeExtract *e;
321     afs_uint64 size;
322     afs_uint64 offset;
323     Inode ino, newino;
324     afs_uint32 newVn;
325
326     fdP = IH_OPEN(vol->vnodeIndex[class].handle);
327     if (!fdP) {
328         Log("Couldn't open %s Index of volume %u\n", 
329                 class ? "small":"large", V_id(vol));
330         code = EIO;
331         goto Bad_Copy;
332     }
333     newfdP = IH_OPEN(newvol->vnodeIndex[class].handle);
334     if (!newfdP) {
335         Log("Couldn't open %s Index of volume %u\n", 
336                 class ? "small":"large", V_id(newvol));
337         code = EIO;
338         goto Bad_Copy;
339     }
340     size = FDH_SIZE(fdP);
341
342     for (i=0; i<length; i++) {
343         e = &list[i];
344         if (e->flag) {
345             afs_uint64 size;
346             offset = (e->vN + 1 - class) << (vcp->logSize -1);
347             if (FDH_SEEK(fdP, offset, 0) != offset
348              || FDH_READ(fdP, vnode, vcp->diskSize) != vcp->diskSize) {
349                 Log("Couldn't read in %s Index of volume %u at offset\n", 
350                         class ? "small":"large", V_id(vol), offset);
351                 code = EIO;
352                 goto Bad_Copy;
353             }
354             if (e->flag & PARENT) {
355                 /* 
356                  *   do a preventive copy on write for later update 
357                  */ 
358                 IHandle_t *newh = 0;
359                 IHandle_t *h = 0;
360                 FdHandle_t *infdP = 0;
361                 FdHandle_t *outfdP = 0;
362                 char *tbuf = malloc(2048);
363                 Inode nearInode;
364 #if defined(NEARINODE_HINT)
365                 V_pref(vol,nearInode)
366 #endif
367
368                 newino = IH_CREATE(V_linkHandle(vol), V_device(vol),
369                                 VPartitionPath(V_partition(vol)),
370                                 nearInode, V_parentId(vol), 
371                                 e->vN, vnode->uniquifier,
372                                 vnode->dataVersion);
373                 IH_INIT(newh, V_device(vol), V_parentId(vol), newino);
374                 ino = VNDISK_GET_INO(vnode);
375                 IH_INIT(h, V_device(vol), V_parentId(vol), ino);
376                 code = copyDir(m, h, newh);
377                 if (code)
378                     goto Bad_Copy;
379                 /* Now update the vnode and write it back to disk */
380                 VNDISK_SET_INO(vnode, newino);
381                 vnode->cloned = 0;
382                 if (FDH_SEEK(fdP, offset, 0) != offset
383                  || FDH_WRITE(fdP, vnode, vcp->diskSize) != vcp->diskSize) {
384                     Log("Couldn't write in %s Index of volume %u at offset\n", 
385                             class ? "small":"large", V_id(vol), offset);
386                     code = EIO;
387                     goto Bad_Copy;
388                 }
389                 memcpy(parVnode, vnode, sizeof(struct VnodeDiskObject));
390             }
391             if (e->flag & NEEDED && e->vN != where) {
392                 VNDISK_GET_LEN(size, vnode);
393                 *blocks += (size + 0x3ff) >> 10;
394                 ino = VNDISK_GET_INO(vnode);
395                 if (ino) {
396                     IHandle_t *h, *newh;
397                     Inode nearInode;
398 #if defined(NEARINODE_HINT)
399                     V_pref(vol,nearInode)
400 #endif
401                     IH_INIT(h, vol->device, V_parentId(vol), ino);
402                     if (e->parent == where) 
403                         vnode->parent = 1;
404                     newino = IH_CREATE(V_linkHandle(newvol), V_device(newvol),
405                                 VPartitionPath(V_partition(newvol)),
406                                 nearInode, V_parentId(newvol), 
407                                 e->vN, vnode->uniquifier,
408                                 vnode->dataVersion);
409                     if (!VALID_INO(newino)) {
410                         Log("IH_CREATE failed for %u.%u.%u\n", 
411                             V_id(newvol), e->vN, vnode->uniquifier);
412                         code = EIO;
413                         goto Bad_Copy;
414                     }
415                     nearInode = newino;
416                     IH_INIT(newh, newvol->device, V_parentId(newvol), newino);
417                     code = namei_replace_file_by_hardlink(newh, h);
418                     VNDISK_SET_INO(vnode, newino);
419 #ifdef AFS_RXOSD_SUPPORT
420                 } else {
421                     code = osd_split_objects(vol, newvol, vnode, e->vN);
422 #endif /*  AFS_RXOSD_SUPPORT */
423                 }
424                 if (code)
425                     goto Bad_Copy;
426                 if (e->flag & CHANGEPARENT)
427                     vnode->parent = 1; /* in new root-directory */
428                 vnode->cloned = 0;
429                 if (FDH_SEEK(newfdP, offset, 0) != offset
430                  || FDH_WRITE(newfdP, vnode, vcp->diskSize) != vcp->diskSize) {
431                     Log("Couldn't write in %s Index of volume %u to offset\n", 
432                             class ? "small":"large", V_id(newvol), 
433                             offset);
434                     code = EIO;
435                     goto Bad_Copy;
436                 }
437             }
438         }
439     }
440     /*
441      *  Now copy the root directory from old to new volume 
442      */
443     if (class == vLarge) {
444         IHandle_t *h, *newh;
445         char buf2[SIZEOF_LARGEDISKVNODE];
446         struct VnodeDiskObject *vnode2 = (struct VnodeDiskObject *)&buf2;
447         afs_uint64 newoffset;
448         
449         newoffset = vcp->diskSize;
450         if (FDH_SEEK(newfdP, newoffset, 0) != newoffset
451          || FDH_READ(newfdP, vnode2, vcp->diskSize) != vcp->diskSize) {
452             Log("splitvolume: couldn't read in large Index of new volume %u at offset %u\n", 
453                     V_id(newvol), vcp->diskSize);
454             code = EIO;
455             goto Bad_Copy;
456         }
457         offset = (where + 1 - class) << (vcp->logSize -1);
458         if (FDH_SEEK(fdP, offset, 0) != offset
459          || FDH_READ(fdP, vnode, vcp->diskSize) != vcp->diskSize) {
460             Log("Couldn't read in large Index of old volume %u at offset\n", 
461                         V_id(vol), offset);
462             code = EIO;
463             goto Bad_Copy;
464         }
465         VNDISK_GET_LEN(size, vnode);
466         *blocks += (size + 0x3ff) >> 10;
467         ino = VNDISK_GET_INO(vnode);
468         IH_INIT(h, vol->device, V_parentId(vol), ino);
469         newino = VNDISK_GET_INO(vnode2);
470         IH_INIT(newh, newvol->device, V_parentId(newvol), newino);
471         code = copyDir(m, h, newh);
472         if (code) {
473             Log("splitvolume: copyDir failed for new root from %u.u.u to %u.1.1\n", 
474                         V_id(vol), where, vnode->uniquifier, V_id(newvol));
475             code = EIO;
476             goto Bad_Copy;
477         }
478         VNDISK_SET_INO(vnode, newino);
479         vnode->uniquifier = 1;
480         vnode->cloned = 0;
481         vnode->parent = vnode2->parent;
482         vnode->serverModifyTime = vnode2->serverModifyTime;
483         if (FDH_SEEK(newfdP, newoffset, 0) != newoffset
484           || FDH_WRITE(newfdP, vnode, vcp->diskSize) != vcp->diskSize) {
485             Log("splitvolume: couldn't write in large Index of %u at offset %u\n", 
486                     V_id(newvol), vcp->diskSize);
487             code = EIO;
488         } 
489     }
490 Bad_Copy:
491     if (fdP)
492         FDH_CLOSE(fdP);
493     if (newfdP)
494         FDH_CLOSE(newfdP);
495     return code;
496 }
497
498 afs_int32
499 findName(Volume *vol, struct VnodeDiskObject *vd, afs_uint32 vN, afs_uint32 un, 
500                 char *name,afs_int32 length)
501 {
502     afs_int32 code;
503     Inode ino;
504     DirHandle dir;
505
506     ino = VNDISK_GET_INO(vd);
507     SetSalvageDirHandle(&dir, V_id(vol), V_device(vol), ino);
508
509     code = InverseLookup(&dir, vN, un, name, length);
510     FidZap(&dir);   
511     return code;
512 }
513
514 afs_int32
515 createMountpoint(Volume *vol, Volume *newvol, struct VnodeDiskObject *parent, 
516                 afs_uint32 vN,  struct VnodeDiskObject *vd, char *name)
517 {
518     afs_int32 code;
519     Inode ino, newino;
520     DirHandle dir;
521     IHandle_t *h;
522     struct VnodeDiskObject vnode;
523     FdHandle_t *fdP, *fdP2;
524     afs_uint64 offset, size;
525     afs_int32 class = vSmall;
526     struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
527     Inode nearInode = 0;
528     AFSFid fid;
529     struct timeval now;
530     afs_uint32 newvN;
531     char symlink[32];
532
533     TM_GetTimeOfDay(&now, 0);
534     fdP = IH_OPEN(vol->vnodeIndex[vSmall].handle);
535     if (!fdP) {
536         Log("split volume: error opening small vnode index of %u\n", V_id(vol));
537         return EIO;
538     }
539     offset = vcp->diskSize;
540     if (FDH_SEEK(fdP, offset, 0) != offset) {
541         Log("split volume: error seeking in small vnode index of %u\n", V_id(vol));
542         return EIO;
543     }
544     while (1) {
545         if (FDH_READ(fdP, &vnode, vcp->diskSize) != vcp->diskSize)
546             break;
547         if (vnode.type == vNull)
548             break;
549         offset += vcp->diskSize;
550     }
551     memset(&vnode, 0, sizeof(vnode));
552     vnode.type = vSymlink;
553     V_nextVnodeUnique(vol)++;
554     vnode.uniquifier = V_nextVnodeUnique(vol);
555     vnode.author = vd->author;
556     vnode.owner = vd->owner;
557     vnode.group = vd->group;
558     vnode.modeBits = 0644;
559     vnode.unixModifyTime = now.tv_sec;
560     vnode.serverModifyTime = now.tv_sec;
561     vnode.dataVersion = 1;
562     vnode.linkCount = 1;
563     vnode.parent = vN;
564
565     newvN = (offset >> (VnodeClassInfo[class].logSize - 1)) - 1 + class;
566 #if defined(NEARINODE_HINT)
567     V_pref(vol,nearInode)
568 #endif
569     newino = IH_CREATE(V_linkHandle(vol), V_device(vol),
570                 VPartitionPath(V_partition(vol)), nearInode,
571                 V_parentId(vol), newvN, vnode.uniquifier, 1);
572     
573     IH_INIT(h, V_device(vol), V_parentId(vol), newino);
574     fdP2 = IH_OPEN(h);
575     if (!fdP2) {
576         Log("split volume: couldn't open inode for mountpoint %u.%u.%u\n", 
577                 V_id(vol), newvN, vnode.uniquifier);
578         return EIO;
579     }
580     FDH_SEEK(fdP2, 0, 0);
581     sprintf(&symlink, "#%s", V_name(newvol));
582     size = strlen(symlink) + 1;
583     if (FDH_WRITE(fdP2, &symlink, size) != size) {
584         Log("split volume: couldn't write mountpoint %u.%u.%u\n", 
585                 V_id(vol), newvN, vnode.uniquifier);
586         return EIO;
587     }
588     FDH_REALLYCLOSE(fdP2);
589     IH_RELEASE(h);
590     VNDISK_SET_INO(&vnode, newino);
591     VNDISK_SET_LEN(&vnode, size);
592 #ifndef AFS_RXOSD_SUPPORT
593     vnode.vnodeMagic = SMALLVNODEMAGIC;
594 #endif    
595     if (FDH_SEEK(fdP, offset, 0) != offset
596       || FDH_WRITE(fdP, &vnode, vcp->diskSize) != vcp->diskSize) {
597         Log("split volume: couldn't write vnode for mountpoint %u.%u.%u\n", 
598                 V_id(vol), newvN, vnode.uniquifier);
599         return EIO;
600     }
601     FDH_REALLYCLOSE(fdP);
602
603     fid.Volume = V_id(vol);
604     fid.Vnode = newvN;
605     fid.Unique = vnode.uniquifier;
606
607     /*
608      * Now  update the parent directory.
609      */
610
611     ino = VNDISK_GET_INO(parent);
612     SetSalvageDirHandle(&dir, V_id(vol), V_device(vol), ino);
613
614     code = Delete(&dir, name);
615     if (code) {
616         Log("splitvolume: couldn't delete directory entry for %s in %u.%u.%u, code = %d\n",
617                         name, V_id(vol), vN, parent->uniquifier, code);
618         return code;
619     }
620     code = Create(&dir, name, &fid);
621     FidZap(&dir);   
622  
623     class = vLarge;
624     vcp = &VnodeClassInfo[class];
625     fdP = IH_OPEN(vol->vnodeIndex[class].handle);
626     offset = (vN + 1 - class) << (vcp->logSize -1);
627     parent->dataVersion++;
628     if (FDH_SEEK(fdP, offset, 0) != offset
629       || FDH_WRITE(fdP, parent, vcp->diskSize) != vcp->diskSize) {
630         Log("split volume: couldn't write vnode for parent directory %u.%u.%u\n", 
631                 V_id(vol), vN, parent->uniquifier);
632         return EIO;
633     }
634     FDH_REALLYCLOSE(fdP);
635     return code;
636 }
637
638 afs_int32 deleteVnodes(Volume *vol, afs_int32 class, 
639                         struct VnodeExtract *list, afs_int32 length,
640                         afs_uint64 *blocks)
641 {
642     afs_int32 i, code = 0;
643     char buf[SIZEOF_LARGEDISKVNODE];
644     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)&buf;
645     FdHandle_t *fdP = 0;
646     struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
647     struct VnodeExtract *e;
648     afs_uint64 size;
649     afs_uint64 offset;
650     Inode ino;
651
652     fdP = IH_OPEN(vol->vnodeIndex[class].handle);
653     if (!fdP) {
654         Log("Couldn't open %s Index of volume %u\n", 
655                 class ? "small":"large", V_id(vol));
656         code = EIO;
657         goto Bad_Delete;
658     }
659     size = FDH_SIZE(fdP);
660
661     for (i=0; i<length; i++) {
662         e = &list[i];
663         if (e->flag & NEEDED) {
664             afs_uint64 size;
665             offset = (e->vN + 1 - class) << (vcp->logSize -1);
666             if (FDH_SEEK(fdP, offset, 0) != offset) {
667                 Log("Couldn't seek in %s Index of volume %u to offset\n", 
668                         class ? "small":"large", V_id(vol), offset);
669                 code = EIO;
670                 goto Bad_Delete;
671             }
672             if (FDH_READ(fdP, vnode, vcp->diskSize) != vcp->diskSize) {
673                 Log("Couldn't read in %s Index of volume %u at offset\n", 
674                         class ? "small":"large", V_id(vol), offset);
675                 code = EIO;
676                 goto Bad_Delete;
677             }
678             VNDISK_GET_LEN(size, vnode);
679             *blocks += (size + 0x3ff) >> 10;
680             ino = VNDISK_GET_INO(vnode);
681             if (ino) {
682                 IHandle_t *h, *newh;
683                 IH_INIT(h, vol->device, V_parentId(vol), ino);
684                     IH_DEC(h, ino, V_parentId(vol));
685 #ifdef AFS_RXOSD_SUPPORT
686             } else {
687                 code = osdRemove(vol, vnode, e->vN);
688 #endif /*  AFS_RXOSD_SUPPORT */
689             }
690             memset(vnode, 0, vcp->diskSize);
691             vnode->type = vNull;
692             if (FDH_SEEK(fdP, offset, 0) != offset 
693               || FDH_WRITE(fdP, vnode, vcp->diskSize) != vcp->diskSize) {
694                    Log("Couldn't write in %s Index of volume %u to offset\n", 
695                             class ? "small":"large", V_id(vol), 
696                             offset);
697             }
698         }
699     }
700 Bad_Delete:
701     if (fdP)
702         FDH_CLOSE(fdP);
703     return code;
704 }
705 afs_int32 
706 split_volume(struct rx_call *call, Volume *vol, Volume *newvol, 
707                 afs_uint32 where, afs_int32 verbose)
708 {
709     afs_int32 code = 0;
710     struct VnodeExtract *dirList = 0;
711     struct VnodeExtract *fileList = 0;
712     afs_uint64 blocks = 0;
713     afs_uint32 filesNeeded, dirsNeeded;
714     afs_uint32 dl, fl;
715     char buf[SIZEOF_LARGEDISKVNODE];
716     char buf2[SIZEOF_LARGEDISKVNODE];
717     struct VnodeDiskObject *rootVnode = (struct VnodeDiskObject *)&buf;
718     struct VnodeDiskObject *parVnode = (struct VnodeDiskObject *)&buf2;
719     char name[256];
720     afs_uint32 parent;
721     struct Msg *m;
722
723     m = (struct Msg *) malloc(sizeof(struct Msg));
724     memset(m, 0, sizeof(struct Msg));
725     m->call = call;
726     m->verbose = verbose;
727
728     /* 
729      *  First step: planning
730      *
731      *  Find out which directories will belong to the new volume
732      *
733      */
734     if (verbose) {
735         sprintf(m->line, 
736                 "1st step: extract vnode essence from large vnode file\n");
737         rx_Write(m->call, m->line, strlen(m->line));
738     }
739
740     code = ExtractVnodes(m, vol, vLarge, &dirList, &dl, where, rootVnode, 
741                         &parent, parVnode);
742     if (code) {
743         sprintf(m->line, 
744                 "ExtractVnodes failed for %u for directories with code %d\n",
745                 V_id(vol), code);
746         rx_Write(m->call, m->line, strlen(m->line));
747         return code;
748     }
749
750     if (verbose) {
751         sprintf(m->line, "2nd step: look for name of vnode %u in directory %u.%u.%u\n",
752                 where, V_id(vol), parent, parVnode->uniquifier); 
753         rx_Write(m->call, m->line, strlen(m->line));
754     }
755     code = findName(vol, parVnode, where, rootVnode->uniquifier, 
756                         &name,  sizeof(name));
757     if (code) {
758         sprintf(m->line, 
759                 "splitvolume: could'nt find name of %u in directory %u.%u.%u.\n",
760                 where, V_id(vol), parent, parVnode->uniquifier); 
761         rx_Write(m->call, m->line, strlen(m->line));
762         return code;
763     }
764     if (verbose) {
765         sprintf(m->line, "name of %u is %s\n", where, name); 
766         rx_Write(m->call, m->line, strlen(m->line));
767     }
768
769     if (verbose) {
770         sprintf(m->line, "3rd step: find all directory vnodes belonging to the subtree under %u \"%s\"\n", 
771                         where, name);
772         rx_Write(m->call, m->line, strlen(m->line));
773     }
774     code = FindVnodes(m, where, dirList, dl, dirList, dl, &dirsNeeded, 1);
775     if (code) {
776         sprintf(m->line, 
777                 "FindVnodes for directories failed with code %d\n", code);
778         rx_Write(m->call, m->line, strlen(m->line));
779         return code;
780     }
781
782     if (verbose) {
783         sprintf(m->line, "4th step extract vnode essence from small vnode file\n");
784         rx_Write(m->call, m->line, strlen(m->line));
785     }
786     code = ExtractVnodes(m, vol, vSmall, &fileList, &fl, where, 0, 0, 0);
787     if (code) {
788         sprintf(m->line, 
789                 "ExtractVnodes failed for %u for files with code %d\n",
790                 V_id(vol), code);
791         rx_Write(m->call, m->line, strlen(m->line));
792         return code;
793     }
794     if (verbose) {
795         sprintf(m->line, "5th step: find all small vnodes belonging to the subtree under %u \"%s\"\n", 
796                         where, name);
797         rx_Write(m->call, m->line, strlen(m->line));
798     }
799     FindVnodes(m, where, fileList, fl, dirList, dl, &filesNeeded, 0);
800
801     /* 
802      *  Third step: create hard links for all files needed
803      *
804      */
805
806     V_destroyMe(newvol) = DESTROY_ME;
807     V_inService(newvol) = 0;
808     if (verbose) {
809         sprintf(m->line, "6th step: create hard links in the AFSIDat tree between files of the old and new volume\n");
810         rx_Write(m->call, m->line, strlen(m->line));
811     }
812     code = copyVnodes(m, vol, newvol, 1, fileList, fl, where, &blocks, 0); 
813     if (code) {
814         sprintf(m->line, "copyVnodes for files failed with code %d\n", code);
815         rx_Write(m->call, m->line, strlen(m->line));
816         return code;
817     }
818         
819     /* 
820      *  Forth step: create hard links for all directories and copy 
821      *  split directory to new root directory
822      */
823
824     if (verbose) {
825         sprintf(m->line, "7th step: create hard links in the AFSIDat tree between directories of the old and new volume and make dir %u to new volume's root directory.\n",
826                 where);
827         rx_Write(m->call, m->line, strlen(m->line));
828     }
829     code = copyVnodes(m, vol, newvol, 0, dirList, dl, where, &blocks, parVnode); 
830     if (code) {
831         sprintf(m->line, "copyVnodes for directories failed with code %d\n", code);
832         rx_Write(m->call, m->line, strlen(m->line));
833         return code;
834     }
835
836     /*
837      *  Finalize new volume
838      *
839      */
840     if (verbose) {
841         sprintf(m->line, "8th step: write new volume's metadata to disk\n");
842         rx_Write(m->call, m->line, strlen(m->line));
843     }
844
845     V_diskused(newvol) = blocks;
846     /*V_osdFlag(newvol) = V_osdFlag(vol);*/
847     V_filecount(newvol) = filesNeeded + dirsNeeded;
848     V_destroyMe(newvol) = 0;
849     V_maxquota(newvol) = V_maxquota(vol);
850     V_uniquifier(newvol) = V_uniquifier(vol);
851     V_inService(newvol) = 1;
852     VUpdateVolume(&code, newvol);
853
854     /*
855      *  Sixth step: change directory entry in old volume:
856      *  rename old tree and create mount point for new volume.
857      */
858     if (verbose) {
859         sprintf(m->line, "9th step: create mountpoint \"%s\" for new volume in old volume's directory %u.\n", name, parent);
860         rx_Write(m->call, m->line, strlen(m->line));
861     }
862     
863     code = createMountpoint(vol, newvol, parVnode, parent, rootVnode, name);
864     if (code) {
865         sprintf(m->line, "createMountpoint failed with code %d\n", code);
866         rx_Write(m->call, m->line, strlen(m->line));
867         return code;
868     }
869     /*
870      * Now both volumes should be ready and consistent, but the old volume
871      * contains still the vnodes and data we transferred into the new one.
872      * Delete orphaned vnodes and data.
873      */ 
874
875     blocks = 0;
876     if (verbose) {
877         sprintf(m->line, "10th step: delete large vnodes belonging to subtree in the old volume.\n");
878         rx_Write(m->call, m->line, strlen(m->line));
879     }
880     deleteVnodes(vol, vLarge, dirList, dl, &blocks);
881     if (verbose) {
882         sprintf(m->line, "11th step: delete small vnodes belonging to subtree in the old volume.\n");
883         rx_Write(m->call, m->line, strlen(m->line));
884     }
885     deleteVnodes(vol, vSmall, fileList, fl, &blocks);
886     V_diskused(vol) -= blocks;
887     V_filecount(vol) -= (filesNeeded + dirsNeeded + 1);
888     VUpdateVolume(&code, vol);
889
890     sprintf(m->line, "Finished!\n");
891     rx_Write(m->call, m->line, strlen(m->line));
892     m->line[0] = 0;
893     m->line[1] = 0;
894     m->line[2] = 0;
895     m->line[3] = 0;
896     rx_Write(m->call, m->line, 4);
897     free(m);
898     return code;
899 }