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