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