vol: fix everything to use volumeid type
[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/dir.h>
19 #include <rx/xdr.h>
20 #include <rx/rx_queue.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 %" AFS_VOLID_FMT "\n",
89                 class ? "small":"large", afs_printable_VolumeId_lu(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 = calloc(size / vcp->diskSize, sizeof(struct VnodeExtract));
96     if (!(*list)) {
97         code = ENOMEM;
98         goto Bad_Extract;
99     }
100     stream = FDH_FDOPEN(fdP, "r");
101     if (!stream) {
102         sprintf(m->line, "Couldn't stream open %s Index of volume %" AFS_VOLID_FMT "\n",
103                 class ? "small":"large", afs_printable_VolumeId_lu(V_id(vol)));
104         rx_Write(m->call, m->line, strlen(m->line));
105         code = EIO;
106         goto Bad_Extract;
107     }
108     code = STREAM_ASEEK(stream, vcp->diskSize);
109     if (code)
110         goto Bad_Extract;
111
112     offset = vcp->diskSize;
113     e = *list;
114     while (!STREAM_EOF(stream)) {
115         afs_int32 vN = (offset >> (vcp->logSize -1)) - 1 + class;
116         if (STREAM_READ(vnode, vcp->diskSize, 1, stream) == 1) {
117             if (vnode->type != vNull) {
118                 e->vN = vN;
119                 e->parent = vnode->parent;
120                 if (vN == where && class == vLarge) {
121                     memcpy(vd, vnode, vcp->diskSize);
122                     *parent = vnode->parent;
123                 }
124                 e++;
125             }
126             offset += vcp->diskSize;
127         }
128     }
129     *length = (e - *list);
130     if (class == vLarge) {
131         if (*parent) {
132             offset = (*parent + 1 - class) << (vcp->logSize -1);
133             code = STREAM_ASEEK(stream, offset);
134             if (STREAM_READ(vnode, vcp->diskSize, 1, stream) == 1)
135                 memcpy(parentvd, vnode, vcp->diskSize);
136             else
137                 code = EIO;
138         } else {
139             sprintf(m->line, "SplitVolume: extract didn't see directory %u\n", where);
140             rx_Write(m->call, m->line, strlen(m->line));
141             code = ENOENT;
142         }
143     }
144     if (m->verbose) {
145         sprintf(m->line, "Volume %" AFS_VOLID_FMT " has %u %s vnodes in volume %" AFS_VOLID_FMT "\n",
146                 afs_printable_VolumeId_lu(V_parentId(vol)), *length,
147                 class? "small":"large", afs_printable_VolumeId_lu(V_id(vol)));
148         rx_Write(m->call, m->line, strlen(m->line));
149     }
150
151 Bad_Extract:
152     if (stream)
153         STREAM_CLOSE(stream);
154     if (fdP)
155         FDH_CLOSE(fdP);
156     if (code) {
157         free(*list);
158         *list = 0;
159     }
160     return code;
161 }
162
163 static afs_int32
164 FindVnodes(struct Msg *m, afs_uint32 where,
165            struct VnodeExtract *list, afs_int32 length,
166            struct VnodeExtract *dlist, afs_int32 dlength,
167            afs_uint32 *needed, afs_int32 class)
168 {
169     afs_int32 i, j, found = 0;
170     afs_int32 parent = 0;
171
172     *needed = 0;
173     for (i=0; i<length; i++) {
174         if (list[i].vN == where) {        /* dir to be replaced by mount point */
175             list[i].flag |= NEEDED;
176             parent = list[i].parent;
177             found = 1;
178             (*needed)++;
179         }
180         if (list[i].parent == where) {          /* all 1st generation children */
181             list[i].flag |= (NEEDED + CHANGEPARENT);
182             (*needed)++;
183         }
184     }
185     if (list[0].vN & 1) {               /* only for directories */
186         if (!found) {
187             sprintf(m->line,
188                 "SplitVolume: directory %u where to start new volume not found\n",
189                  where);
190             rx_Write(m->call, m->line, strlen(m->line));
191             return ENOENT;
192         }
193         found = 0;
194         for (i=0; i<length; i++) {
195             if (list[i].vN == parent) { /* dir where to create mount point */
196                 list[i].flag |= PARENT;
197                 found = 1;
198                 break;
199             }
200         }
201         if (!found) {
202             sprintf(m->line, "SplitVolume: parent directory %u not found\n",
203                         parent);
204             rx_Write(m->call, m->line, strlen(m->line));
205             return ENOENT;
206         }
207     }
208     found = 1;
209     while (found) {
210         found = 0;
211         for (i=0; i<dlength; i++) {
212             if (!(dlist[i].flag & NEEDED)) /* dirs to remain in old volume */
213                 continue;
214             for (j=0; j<length; j++) {
215                 if (list[j].parent == dlist[i].vN && !(list[j].flag & NEEDED)) {
216                     list[j].flag |= NEEDED;
217                     (*needed)++;
218                     found = 1;
219                 }
220             }
221         }
222     }
223     if (m->verbose) {
224         sprintf(m->line, "%u %s vnodes will go into the new volume\n",
225                         *needed, class ? "small" : "large");
226         rx_Write(m->call, m->line, strlen(m->line));
227     }
228     return 0;
229 }
230
231 static afs_int32
232 copyDir(struct Msg *m, IHandle_t *inh, IHandle_t *outh)
233 {
234     FdHandle_t *infdP, *outfdP;
235     char *tbuf;
236     afs_sfsize_t size;
237     afs_foff_t offset;
238
239     infdP = IH_OPEN(inh);
240     if (!infdP) {
241         sprintf(m->line, "Couldn't open input directory %" AFS_VOLID_FMT ".%u.%u\n",
242                 afs_printable_VolumeId_lu(infdP->fd_ih->ih_vid),
243                 (afs_uint32)(infdP->fd_ih->ih_ino & NAMEI_VNODEMASK),
244                 (afs_uint32)(infdP->fd_ih->ih_ino >> NAMEI_UNIQSHIFT));
245         rx_Write(m->call, m->line, strlen(m->line));
246         return EIO;
247     }
248     outfdP = IH_OPEN(outh);
249     /*
250      * In case that a file with the same (NAMEI) name existed before and is still
251      * open outfdP may point to the wrong (unlinked) file. To make sure we write
252      * into the correct file it's safer to 1st FDH_REALLYCLOSE it and then to
253      * re-open it.
254      */
255     if (outfdP)
256         FDH_REALLYCLOSE(outfdP);
257     outfdP = IH_OPEN(outh);
258     if (!outfdP) {
259         sprintf(m->line, "Couldn't open output directory %" AFS_VOLID_FMT ".%u.%u\n",
260                 afs_printable_VolumeId_lu(outfdP->fd_ih->ih_vid),
261                 (afs_uint32)(outfdP->fd_ih->ih_ino & NAMEI_VNODEMASK),
262                 (afs_uint32)(outfdP->fd_ih->ih_ino >> NAMEI_UNIQSHIFT));
263         rx_Write(m->call, m->line, strlen(m->line));
264         FDH_REALLYCLOSE(infdP);
265         return EIO;
266     }
267     tbuf = malloc(2048);
268     offset = 0;
269     size = FDH_SIZE(infdP);
270     while (size) {
271         size_t tlen;
272         tlen = size > 2048 ? 2048 : size;
273         if (FDH_PREAD(infdP, tbuf, tlen, offset) != tlen) {
274             sprintf(m->line, "Couldn't read directory %" AFS_VOLID_FMT ".%u.%u\n",
275                     afs_printable_VolumeId_lu(infdP->fd_ih->ih_vid),
276                     (afs_uint32)(infdP->fd_ih->ih_ino & NAMEI_VNODEMASK),
277                     (afs_uint32)(infdP->fd_ih->ih_ino >> NAMEI_UNIQSHIFT));
278             rx_Write(m->call, m->line, strlen(m->line));
279             FDH_REALLYCLOSE(infdP);
280             FDH_REALLYCLOSE(outfdP);
281             free(tbuf);
282             return EIO;
283         }
284         if (FDH_PWRITE(outfdP, tbuf, tlen, offset) != tlen) {
285             sprintf(m->line, "Couldn't write directory %" AFS_VOLID_FMT ".%u.%u\n",
286                     afs_printable_VolumeId_lu(outfdP->fd_ih->ih_vid),
287                     (afs_uint32)(outfdP->fd_ih->ih_ino & NAMEI_VNODEMASK),
288                     (afs_uint32)(outfdP->fd_ih->ih_ino >> NAMEI_UNIQSHIFT));
289             rx_Write(m->call, m->line, strlen(m->line));
290             FDH_REALLYCLOSE(infdP);
291             FDH_REALLYCLOSE(outfdP);
292             free(tbuf);
293             return EIO;
294         }
295         size -= tlen;
296         offset += tlen;
297     }
298     free(tbuf);
299     FDH_CLOSE(outfdP);
300     FDH_REALLYCLOSE(infdP);
301     return 0;
302 }
303
304 afs_int32 copyVnodes(struct Msg *m, Volume *vol, Volume *newvol,
305                         afs_int32 class,
306                         struct VnodeExtract *list, afs_int32 length,
307                         afs_int32 where, afs_uint64 *blocks,
308                         struct VnodeDiskObject *parVnode)
309 {
310     afs_int32 i, code = 0;
311     char buf[SIZEOF_LARGEDISKVNODE];
312     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)&buf;
313     FdHandle_t *fdP = 0;
314     FdHandle_t *newfdP = 0;
315     struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
316     struct VnodeExtract *e;
317     afs_sfsize_t size;
318     afs_uint64 offset;
319     Inode ino, newino;
320
321     fdP = IH_OPEN(vol->vnodeIndex[class].handle);
322     if (!fdP) {
323         Log("Couldn't open %s Index of volume %" AFS_VOLID_FMT "\n",
324             class ? "small":"large", afs_printable_VolumeId_lu(V_id(vol)));
325         code = EIO;
326         goto Bad_Copy;
327     }
328     newfdP = IH_OPEN(newvol->vnodeIndex[class].handle);
329     if (!newfdP) {
330         Log("Couldn't open %s Index of volume %" AFS_VOLID_FMT "\n",
331             class ? "small":"large", afs_printable_VolumeId_lu(V_id(newvol)));
332         code = EIO;
333         goto Bad_Copy;
334     }
335     size = FDH_SIZE(fdP);
336
337     for (i=0; i<length; i++) {
338         e = &list[i];
339         if (e->flag) {
340             afs_uint64 size;
341             offset = (e->vN + 1 - class) << (vcp->logSize -1);
342             if (FDH_PREAD(fdP, vnode, vcp->diskSize, offset) != vcp->diskSize) {
343                 Log("Couldn't read in %s Index of volume %" AFS_VOLID_FMT " at offset %llu\n",
344                     class ? "small":"large",
345                     afs_printable_VolumeId_lu(V_id(vol)), offset);
346                 code = EIO;
347                 goto Bad_Copy;
348             }
349             if (e->flag & PARENT) {
350                 /*
351                  *   do a preventive copy on write for later update
352                  */
353                 IHandle_t *newh = 0;
354                 IHandle_t *h = 0;
355 #if defined(NEARINODE_HINT) && !defined(AFS_NAMEI_ENV)
356                 Inode nearInode;
357                 V_pref(vol,nearInode)
358 #endif
359
360                 newino = IH_CREATE(V_linkHandle(vol), V_device(vol),
361                                 VPartitionPath(V_partition(vol)),
362                                 nearInode, V_parentId(vol),
363                                 e->vN, vnode->uniquifier,
364                                 vnode->dataVersion);
365                 IH_INIT(newh, V_device(vol), V_parentId(vol), newino);
366                 ino = VNDISK_GET_INO(vnode);
367                 IH_INIT(h, V_device(vol), V_parentId(vol), ino);
368                 code = copyDir(m, h, newh);
369                 if (code)
370                     goto Bad_Copy;
371                 /* Now update the vnode and write it back to disk */
372                 VNDISK_SET_INO(vnode, newino);
373                 vnode->cloned = 0;
374                 if (FDH_PWRITE(fdP, vnode, vcp->diskSize, offset) != vcp->diskSize) {
375                     Log("Couldn't write in %s Index of volume %" AFS_VOLID_FMT " at offset %llu\n",
376                         class ? "small":"large", afs_printable_VolumeId_lu(V_id(vol)), offset);
377                     code = EIO;
378                     goto Bad_Copy;
379                 }
380                 if (parVnode != NULL)
381                     memcpy(parVnode, vnode, sizeof(struct VnodeDiskObject));
382             }
383             if (e->flag & NEEDED && e->vN != where) {
384                 VNDISK_GET_LEN(size, vnode);
385                 *blocks += (size + 0x3ff) >> 10;
386                 ino = VNDISK_GET_INO(vnode);
387                 if (ino) {
388                     IHandle_t *h, *newh;
389                     Inode AFS_UNUSED nearInode;
390 #if defined(NEARINODE_HINT) && !defined(AFS_NAMEI_ENV)
391                     V_pref(vol,nearInode)
392 #endif
393                     IH_INIT(h, vol->device, V_parentId(vol), ino);
394                     if (e->parent == where)
395                         vnode->parent = 1;
396                     newino = IH_CREATE(V_linkHandle(newvol), V_device(newvol),
397                                 VPartitionPath(V_partition(newvol)),
398                                 nearInode, V_parentId(newvol),
399                                 e->vN, vnode->uniquifier,
400                                 vnode->dataVersion);
401                     if (!VALID_INO(newino)) {
402                         Log("IH_CREATE failed for %" AFS_VOLID_FMT ".%u.%u\n",
403                             afs_printable_VolumeId_lu(V_id(newvol)), e->vN, vnode->uniquifier);
404                         code = EIO;
405                         goto Bad_Copy;
406                     }
407                     nearInode = newino;
408                     IH_INIT(newh, newvol->device, V_parentId(newvol), newino);
409                     code = namei_replace_file_by_hardlink(newh, h);
410                     VNDISK_SET_INO(vnode, newino);
411 #ifdef AFS_RXOSD_SUPPORT
412                 } else {
413                     code = osd_split_objects(vol, newvol, vnode, e->vN);
414 #endif /*  AFS_RXOSD_SUPPORT */
415                 }
416                 if (code)
417                     goto Bad_Copy;
418                 if (e->flag & CHANGEPARENT)
419                     vnode->parent = 1; /* in new root-directory */
420                 vnode->cloned = 0;
421                 if (FDH_PWRITE(newfdP, vnode, vcp->diskSize, offset) != vcp->diskSize) {
422                     Log("Couldn't write in %s Index of volume %" AFS_VOLID_FMT " to offset %llu\n",
423                         class ? "small":"large", afs_printable_VolumeId_lu(V_id(newvol)), offset);
424                     code = EIO;
425                     goto Bad_Copy;
426                 }
427             }
428         }
429     }
430     /*
431      *  Now copy the root directory from old to new volume
432      */
433     if (class == vLarge) {
434         IHandle_t *h, *newh;
435         char buf2[SIZEOF_LARGEDISKVNODE];
436         struct VnodeDiskObject *vnode2 = (struct VnodeDiskObject *)&buf2;
437         afs_uint64 newoffset;
438
439         newoffset = vcp->diskSize;
440         if (FDH_PREAD(newfdP, vnode2, vcp->diskSize, newoffset) != vcp->diskSize) {
441             Log("splitvolume: couldn't read in large Index of new volume %" AFS_VOLID_FMT " at offset %u\n",
442                 afs_printable_VolumeId_lu(V_id(newvol)), vcp->diskSize);
443             code = EIO;
444             goto Bad_Copy;
445         }
446         offset = (where + 1 - class) << (vcp->logSize -1);
447         if (FDH_PREAD(fdP, vnode, vcp->diskSize, offset) != vcp->diskSize) {
448             Log("Couldn't read in large Index of old volume %" AFS_VOLID_FMT " at offset %llu\n",
449                 afs_printable_VolumeId_lu(V_id(vol)), offset);
450             code = EIO;
451             goto Bad_Copy;
452         }
453         VNDISK_GET_LEN(size, vnode);
454         *blocks += (size + 0x3ff) >> 10;
455         ino = VNDISK_GET_INO(vnode);
456         IH_INIT(h, vol->device, V_parentId(vol), ino);
457         newino = VNDISK_GET_INO(vnode2);
458         IH_INIT(newh, newvol->device, V_parentId(newvol), newino);
459         code = copyDir(m, h, newh);
460         if (code) {
461             Log("splitvolume: copyDir failed for new root from "
462                 "%" AFS_VOLID_FMT ".%u.%u to %" AFS_VOLID_FMT ".1.1\n",
463                 afs_printable_VolumeId_lu(V_id(vol)), where, vnode->uniquifier,
464                 afs_printable_VolumeId_lu(V_id(newvol)));
465             code = EIO;
466             goto Bad_Copy;
467         }
468         VNDISK_SET_INO(vnode, newino);
469         vnode->uniquifier = 1;
470         vnode->cloned = 0;
471         vnode->parent = vnode2->parent;
472         vnode->serverModifyTime = vnode2->serverModifyTime;
473         if (FDH_PWRITE(newfdP, vnode, vcp->diskSize, newoffset) != vcp->diskSize) {
474             Log("splitvolume: couldn't write in large Index of %" AFS_VOLID_FMT " at offset %u\n",
475                 afs_printable_VolumeId_lu(V_id(newvol)), vcp->diskSize);
476             code = EIO;
477         }
478     }
479 Bad_Copy:
480     if (fdP)
481         FDH_CLOSE(fdP);
482     if (newfdP)
483         FDH_CLOSE(newfdP);
484     return code;
485 }
486
487 static afs_int32
488 findName(Volume *vol, struct VnodeDiskObject *vd, afs_uint32 vN,
489          afs_uint32 un, char *name,afs_int32 length)
490 {
491     afs_int32 code;
492     Inode ino;
493     DirHandle dir;
494
495     ino = VNDISK_GET_INO(vd);
496     SetSalvageDirHandle(&dir, V_id(vol), V_device(vol), ino);
497
498     code = afs_dir_InverseLookup(&dir, vN, un, name, length);
499     FidZap(&dir);
500     return code;
501 }
502
503 static afs_int32
504 createMountpoint(Volume *vol, Volume *newvol, struct VnodeDiskObject *parent,
505                 afs_uint32 vN,  struct VnodeDiskObject *vd, char *name)
506 {
507     afs_int32 code;
508     Inode ino, newino;
509     DirHandle dir;
510     IHandle_t *h, *hp;
511     struct VnodeDiskObject vnode;
512     FdHandle_t *fdP, *fdP2;
513     afs_uint64 size;
514     afs_foff_t offset;
515     afs_int32 class = vSmall;
516     struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
517 #if defined(NEARINODE_HINT) && !defined(AFS_NAMEI_ENV)
518     Inode nearInode = 0;
519 #endif
520     AFSFid fid;
521     struct timeval now;
522     afs_uint32 newvN;
523     char symlink[32];
524     ssize_t rc;
525
526     FT_GetTimeOfDay(&now, 0);
527     fdP = IH_OPEN(vol->vnodeIndex[vSmall].handle);
528     if (!fdP) {
529         Log("split volume: error opening small vnode index of %" AFS_VOLID_FMT "\n", afs_printable_VolumeId_lu(V_id(vol)));
530         return EIO;
531     }
532     offset = vcp->diskSize;
533     while (1) {
534         rc = FDH_PREAD(fdP, &vnode, vcp->diskSize, offset);
535         if (rc != vcp->diskSize) {
536             if (rc < 0) {
537                 Log("split volume: error reading small vnode index of %" AFS_VOLID_FMT "\n", afs_printable_VolumeId_lu(V_id(vol)));
538                 return EIO;
539              }
540              if (rc == 0)
541                  break;
542              if (rc < vcp->diskSize)
543                  break;
544         }
545         if (vnode.type == vNull)
546             break;
547         offset += vcp->diskSize;
548     }
549     memset(&vnode, 0, sizeof(vnode));
550     vnode.type = vSymlink;
551     V_nextVnodeUnique(vol)++;
552     vnode.uniquifier = V_nextVnodeUnique(vol);
553     vnode.author = vd->author;
554     vnode.owner = vd->owner;
555     vnode.group = vd->group;
556     vnode.modeBits = 0644;
557     vnode.unixModifyTime = now.tv_sec;
558     vnode.serverModifyTime = now.tv_sec;
559     vnode.dataVersion = 1;
560     vnode.linkCount = 1;
561     vnode.parent = vN;
562
563     newvN = (offset >> (VnodeClassInfo[class].logSize - 1)) - 1 + class;
564 #if defined(NEARINODE_HINT) && !defined(AFS_NAMEI_ENV)
565     V_pref(vol,nearInode)
566 #endif
567     newino = IH_CREATE(V_linkHandle(vol), V_device(vol),
568                 VPartitionPath(V_partition(vol)), nearInode,
569                 V_parentId(vol), newvN, vnode.uniquifier, 1);
570
571     IH_INIT(h, V_device(vol), V_parentId(vol), newino);
572     fdP2 = IH_OPEN(h);
573     if (!fdP2) {
574         Log("split volume: couldn't open inode for mountpoint %" AFS_VOLID_FMT ".%u.%u\n",
575             afs_printable_VolumeId_lu(V_id(vol)), newvN, vnode.uniquifier);
576         return EIO;
577     }
578     sprintf(symlink, "#%s", V_name(newvol));
579     size = strlen(symlink) + 1;
580     if (FDH_PWRITE(fdP2, symlink, size, 0) != size) {
581         Log("split volume: couldn't write mountpoint %" AFS_VOLID_FMT ".%u.%u\n",
582             afs_printable_VolumeId_lu(V_id(vol)), newvN, vnode.uniquifier);
583         return EIO;
584     }
585     FDH_REALLYCLOSE(fdP2);
586     IH_RELEASE(h);
587     VNDISK_SET_INO(&vnode, newino);
588     VNDISK_SET_LEN(&vnode, size);
589 #ifndef AFS_RXOSD_SUPPORT
590     vnode.vnodeMagic = SMALLVNODEMAGIC;
591 #endif
592     if (FDH_PWRITE(fdP, &vnode, vcp->diskSize, offset) != vcp->diskSize) {
593         Log("split volume: couldn't write vnode for mountpoint %" AFS_VOLID_FMT ".%u.%u\n",
594             afs_printable_VolumeId_lu(V_id(vol)), newvN, vnode.uniquifier);
595         return EIO;
596     }
597     FDH_REALLYCLOSE(fdP);
598
599     fid.Volume = V_id(vol);
600     fid.Vnode = newvN;
601     fid.Unique = vnode.uniquifier;
602
603     /*
604      * Now  update the parent directory.
605      */
606
607     ino = VNDISK_GET_INO(parent);
608     SetSalvageDirHandle(&dir, V_id(vol), V_device(vol), ino);
609
610     code = afs_dir_Delete(&dir, name);
611     if (code) {
612         Log("splitvolume: couldn't delete directory entry for %s in %" AFS_VOLID_FMT ".%u.%u, code = %d\n",
613             name, afs_printable_VolumeId_lu(V_id(vol)), vN, parent->uniquifier, code);
614         return code;
615     }
616     code = afs_dir_Create(&dir, name, &fid);
617     FidZap(&dir);
618
619     /* Make sure the directory file doesn't remain open */
620     IH_INIT(hp, V_device(vol), V_parentId(vol), ino);
621     fdP = IH_OPEN(hp);
622     if (fdP)
623         FDH_REALLYCLOSE(fdP);
624     IH_RELEASE(hp);
625
626     class = vLarge;
627     vcp = &VnodeClassInfo[class];
628     fdP = IH_OPEN(vol->vnodeIndex[class].handle);
629     offset = (vN + 1 - class) << (vcp->logSize -1);
630     parent->dataVersion++;
631     if (FDH_PWRITE(fdP, parent, vcp->diskSize, offset) != vcp->diskSize) {
632         Log("split volume: couldn't write vnode for parent directory %" AFS_VOLID_FMT ".%u.%u\n",
633             afs_printable_VolumeId_lu(V_id(vol)), vN, parent->uniquifier);
634         return EIO;
635     }
636     FDH_REALLYCLOSE(fdP);
637     return code;
638 }
639
640 static afs_int32
641 deleteVnodes(Volume *vol, afs_int32 class,
642              struct VnodeExtract *list, afs_int32 length,
643              afs_uint64 *blocks)
644 {
645     afs_int32 i, code = 0;
646     char buf[SIZEOF_LARGEDISKVNODE];
647     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)&buf;
648     FdHandle_t *fdP = 0;
649     struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
650     struct VnodeExtract *e;
651     afs_sfsize_t size;
652     afs_uint64 offset;
653     Inode ino;
654
655     fdP = IH_OPEN(vol->vnodeIndex[class].handle);
656     if (!fdP) {
657         Log("Couldn't open %s Index of volume %" AFS_VOLID_FMT "\n",
658             class ? "small":"large", afs_printable_VolumeId_lu(V_id(vol)));
659         code = EIO;
660         goto Bad_Delete;
661     }
662
663     for (i=0; i<length; i++) {
664         e = &list[i];
665         if (e->flag & NEEDED) {
666             offset = (e->vN + 1 - class) << (vcp->logSize -1);
667             if (FDH_PREAD(fdP, vnode, vcp->diskSize, offset) != vcp->diskSize) {
668                 Log("Couldn't read in %s Index of volume %" AFS_VOLID_FMT " at offset %llu\n",
669                     class ? "small":"large", afs_printable_VolumeId_lu(V_id(vol)), offset);
670                 code = EIO;
671                 goto Bad_Delete;
672             }
673             VNDISK_GET_LEN(size, vnode);
674             *blocks += (size + 0x3ff) >> 10;
675             ino = VNDISK_GET_INO(vnode);
676             if (ino) {
677                 IHandle_t *h;
678                 IH_INIT(h, vol->device, V_parentId(vol), ino);
679                     IH_DEC(h, ino, V_parentId(vol));
680 #ifdef AFS_RXOSD_SUPPORT
681             } else {
682                 code = osdRemove(vol, vnode, e->vN);
683 #endif /*  AFS_RXOSD_SUPPORT */
684             }
685             memset(vnode, 0, vcp->diskSize);
686             vnode->type = vNull;
687             if (FDH_PWRITE(fdP, vnode, vcp->diskSize, offset) != vcp->diskSize) {
688                    Log("Couldn't write in %s Index of volume %" AFS_VOLID_FMT " to offset %llu\n",
689                        class ? "small":"large", afs_printable_VolumeId_lu(V_id(vol)), offset);
690             }
691         }
692     }
693 Bad_Delete:
694     if (fdP)
695         FDH_CLOSE(fdP);
696     return code;
697 }
698
699 afs_int32
700 split_volume(struct rx_call *call, Volume *vol, Volume *newvol,
701              afs_uint32 where, afs_int32 verbose)
702 {
703     Error code = 0;
704     struct VnodeExtract *dirList = 0;
705     struct VnodeExtract *fileList = 0;
706     afs_uint64 blocks = 0;
707     afs_uint32 filesNeeded, dirsNeeded;
708     afs_uint32 dl, fl;
709     char buf[SIZEOF_LARGEDISKVNODE];
710     char buf2[SIZEOF_LARGEDISKVNODE];
711     struct VnodeDiskObject *rootVnode = (struct VnodeDiskObject *)&buf;
712     struct VnodeDiskObject *parVnode = (struct VnodeDiskObject *)&buf2;
713     char name[256];
714     afs_uint32 parent;
715     struct Msg *m;
716
717     m = calloc(1, 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 %" AFS_VOLID_FMT " for directories with code %d\n",
738                 afs_printable_VolumeId_lu(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 %" AFS_VOLID_FMT ".%u.%u\n",
745                 where, afs_printable_VolumeId_lu(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 %" AFS_VOLID_FMT ".%u.%u.\n",
753                 where, afs_printable_VolumeId_lu(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 %" AFS_VOLID_FMT " for files with code %d\n",
783                 afs_printable_VolumeId_lu(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