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