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