c5587745f564a896e4303c4b341b3119d95471f5
[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_uint32 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         return 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 %"
353                     AFS_UINT64_FMT "\n", class ? "small":"large",
354                     V_id(vol), offset);
355                 code = EIO;
356                 goto Bad_Copy;
357             }
358             if (e->flag & PARENT) {
359                 /*
360                  *   do a preventive copy on write for later update
361                  */
362                 IHandle_t *newh = 0;
363                 IHandle_t *h = 0;
364 #if defined(NEARINODE_HINT) && !defined(AFS_NAMEI_ENV)
365                 Inode nearInode;
366                 V_pref(vol,nearInode)
367 #endif
368
369                 newino = IH_CREATE(V_linkHandle(vol), V_device(vol),
370                                 VPartitionPath(V_partition(vol)),
371                                 nearInode, V_parentId(vol),
372                                 e->vN, vnode->uniquifier,
373                                 vnode->dataVersion);
374                 IH_INIT(newh, V_device(vol), V_parentId(vol), newino);
375                 ino = VNDISK_GET_INO(vnode);
376                 IH_INIT(h, V_device(vol), V_parentId(vol), ino);
377                 code = copyDir(m, h, newh);
378                 if (code)
379                     goto Bad_Copy;
380                 /* Now update the vnode and write it back to disk */
381                 VNDISK_SET_INO(vnode, newino);
382                 vnode->cloned = 0;
383                 if (FDH_PWRITE(fdP, vnode, vcp->diskSize, offset) != vcp->diskSize) {
384                     Log("Couldn't write in %s Index of volume %u at offset %"
385                         AFS_UINT64_FMT "\n", class ? "small":"large",
386                         V_id(vol), offset);
387                     code = EIO;
388                     goto Bad_Copy;
389                 }
390                 memcpy(parVnode, vnode, sizeof(struct VnodeDiskObject));
391             }
392             if (e->flag & NEEDED && e->vN != where) {
393                 VNDISK_GET_LEN(size, vnode);
394                 *blocks += (size + 0x3ff) >> 10;
395                 ino = VNDISK_GET_INO(vnode);
396                 if (ino) {
397                     IHandle_t *h, *newh;
398                     Inode nearInode;
399 #if defined(NEARINODE_HINT) && !defined(AFS_NAMEI_ENV)
400                     V_pref(vol,nearInode)
401 #endif
402                     IH_INIT(h, vol->device, V_parentId(vol), ino);
403                     if (e->parent == where)
404                         vnode->parent = 1;
405                     newino = IH_CREATE(V_linkHandle(newvol), V_device(newvol),
406                                 VPartitionPath(V_partition(newvol)),
407                                 nearInode, V_parentId(newvol),
408                                 e->vN, vnode->uniquifier,
409                                 vnode->dataVersion);
410                     if (!VALID_INO(newino)) {
411                         Log("IH_CREATE failed for %u.%u.%u\n",
412                             V_id(newvol), e->vN, vnode->uniquifier);
413                         code = EIO;
414                         goto Bad_Copy;
415                     }
416                     nearInode = newino;
417                     IH_INIT(newh, newvol->device, V_parentId(newvol), newino);
418                     code = namei_replace_file_by_hardlink(newh, h);
419                     VNDISK_SET_INO(vnode, newino);
420 #ifdef AFS_RXOSD_SUPPORT
421                 } else {
422                     code = osd_split_objects(vol, newvol, vnode, e->vN);
423 #endif /*  AFS_RXOSD_SUPPORT */
424                 }
425                 if (code)
426                     goto Bad_Copy;
427                 if (e->flag & CHANGEPARENT)
428                     vnode->parent = 1; /* in new root-directory */
429                 vnode->cloned = 0;
430                 if (FDH_PWRITE(newfdP, vnode, vcp->diskSize, offset) != vcp->diskSize) {
431                     Log("Couldn't write in %s Index of volume %u to offset %"
432                         AFS_UINT64_FMT "\n", class ? "small":"large",
433                         V_id(newvol), offset);
434                     code = EIO;
435                     goto Bad_Copy;
436                 }
437             }
438         }
439     }
440     /*
441      *  Now copy the root directory from old to new volume
442      */
443     if (class == vLarge) {
444         IHandle_t *h, *newh;
445         char buf2[SIZEOF_LARGEDISKVNODE];
446         struct VnodeDiskObject *vnode2 = (struct VnodeDiskObject *)&buf2;
447         afs_uint64 newoffset;
448
449         newoffset = vcp->diskSize;
450         if (FDH_PREAD(newfdP, vnode2, vcp->diskSize, newoffset) != vcp->diskSize) {
451             Log("splitvolume: couldn't read in large Index of new volume %u at offset %u\n",
452                     V_id(newvol), vcp->diskSize);
453             code = EIO;
454             goto Bad_Copy;
455         }
456         offset = (where + 1 - class) << (vcp->logSize -1);
457         if (FDH_PREAD(fdP, vnode, vcp->diskSize, offset) != vcp->diskSize) {
458             Log("Couldn't read in large Index of old volume %u at offset %"
459                 AFS_UINT64_FMT "\n", V_id(vol), offset);
460             code = EIO;
461             goto Bad_Copy;
462         }
463         VNDISK_GET_LEN(size, vnode);
464         *blocks += (size + 0x3ff) >> 10;
465         ino = VNDISK_GET_INO(vnode);
466         IH_INIT(h, vol->device, V_parentId(vol), ino);
467         newino = VNDISK_GET_INO(vnode2);
468         IH_INIT(newh, newvol->device, V_parentId(newvol), newino);
469         code = copyDir(m, h, newh);
470         if (code) {
471             Log("splitvolume: copyDir failed for new root from "
472                 "%u.%u.%u to %u.1.1\n",
473                 V_id(vol), where, vnode->uniquifier, V_id(newvol));
474             code = EIO;
475             goto Bad_Copy;
476         }
477         VNDISK_SET_INO(vnode, newino);
478         vnode->uniquifier = 1;
479         vnode->cloned = 0;
480         vnode->parent = vnode2->parent;
481         vnode->serverModifyTime = vnode2->serverModifyTime;
482         if (FDH_PWRITE(newfdP, vnode, vcp->diskSize, newoffset) != vcp->diskSize) {
483             Log("splitvolume: couldn't write in large Index of %u at offset %u\n",
484                     V_id(newvol), vcp->diskSize);
485             code = EIO;
486         }
487     }
488 Bad_Copy:
489     if (fdP)
490         FDH_CLOSE(fdP);
491     if (newfdP)
492         FDH_CLOSE(newfdP);
493     return code;
494 }
495
496 static afs_int32
497 findName(Volume *vol, struct VnodeDiskObject *vd, afs_uint32 vN,
498          afs_uint32 un, char *name,afs_int32 length)
499 {
500     afs_int32 code;
501     Inode ino;
502     DirHandle dir;
503
504     ino = VNDISK_GET_INO(vd);
505     SetSalvageDirHandle(&dir, V_id(vol), V_device(vol), ino);
506
507     code = InverseLookup(&dir, vN, un, name, length);
508     FidZap(&dir);
509     return code;
510 }
511
512 static afs_int32
513 createMountpoint(Volume *vol, Volume *newvol, struct VnodeDiskObject *parent,
514                 afs_uint32 vN,  struct VnodeDiskObject *vd, char *name)
515 {
516     afs_int32 code;
517     Inode ino, newino;
518     DirHandle dir;
519     IHandle_t *h;
520     struct VnodeDiskObject vnode;
521     FdHandle_t *fdP, *fdP2;
522     afs_uint64 size;
523     afs_foff_t offset;
524     afs_int32 class = vSmall;
525     struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
526 #if defined(NEARINODE_HINT) && !defined(AFS_NAMEI_ENV)
527     Inode nearInode = 0;
528 #endif
529     AFSFid fid;
530     struct timeval now;
531     afs_uint32 newvN;
532     char symlink[32];
533     ssize_t rc;
534
535     FT_GetTimeOfDay(&now, 0);
536     fdP = IH_OPEN(vol->vnodeIndex[vSmall].handle);
537     if (!fdP) {
538         Log("split volume: error opening small vnode index of %u\n", V_id(vol));
539         return EIO;
540     }
541     offset = vcp->diskSize;
542     while (1) {
543         rc = FDH_PREAD(fdP, &vnode, vcp->diskSize, offset);
544         if (rc != vcp->diskSize) {
545             if (rc < 0) {
546                 Log("split volume: error reading small vnode index of %u\n", V_id(vol));
547                 return EIO;
548              }
549              if (rc == 0)
550                  break;
551              if (rc < vcp->diskSize)
552                  break;
553         }
554         if (vnode.type == vNull)
555             break;
556         offset += vcp->diskSize;
557     }
558     memset(&vnode, 0, sizeof(vnode));
559     vnode.type = vSymlink;
560     V_nextVnodeUnique(vol)++;
561     vnode.uniquifier = V_nextVnodeUnique(vol);
562     vnode.author = vd->author;
563     vnode.owner = vd->owner;
564     vnode.group = vd->group;
565     vnode.modeBits = 0644;
566     vnode.unixModifyTime = now.tv_sec;
567     vnode.serverModifyTime = now.tv_sec;
568     vnode.dataVersion = 1;
569     vnode.linkCount = 1;
570     vnode.parent = vN;
571
572     newvN = (offset >> (VnodeClassInfo[class].logSize - 1)) - 1 + class;
573 #if defined(NEARINODE_HINT) && !defined(AFS_NAMEI_ENV)
574     V_pref(vol,nearInode)
575 #endif
576     newino = IH_CREATE(V_linkHandle(vol), V_device(vol),
577                 VPartitionPath(V_partition(vol)), nearInode,
578                 V_parentId(vol), newvN, vnode.uniquifier, 1);
579
580     IH_INIT(h, V_device(vol), V_parentId(vol), newino);
581     fdP2 = IH_OPEN(h);
582     if (!fdP2) {
583         Log("split volume: couldn't open inode for mountpoint %u.%u.%u\n",
584                 V_id(vol), newvN, vnode.uniquifier);
585         return EIO;
586     }
587     sprintf(symlink, "#%s", V_name(newvol));
588     size = strlen(symlink) + 1;
589     if (FDH_PWRITE(fdP2, symlink, size, 0) != size) {
590         Log("split volume: couldn't write mountpoint %u.%u.%u\n",
591                 V_id(vol), newvN, vnode.uniquifier);
592         return EIO;
593     }
594     FDH_REALLYCLOSE(fdP2);
595     IH_RELEASE(h);
596     VNDISK_SET_INO(&vnode, newino);
597     VNDISK_SET_LEN(&vnode, size);
598 #ifndef AFS_RXOSD_SUPPORT
599     vnode.vnodeMagic = SMALLVNODEMAGIC;
600 #endif
601     if (FDH_PWRITE(fdP, &vnode, vcp->diskSize, offset) != vcp->diskSize) {
602         Log("split volume: couldn't write vnode for mountpoint %u.%u.%u\n",
603                 V_id(vol), newvN, vnode.uniquifier);
604         return EIO;
605     }
606     FDH_REALLYCLOSE(fdP);
607
608     fid.Volume = V_id(vol);
609     fid.Vnode = newvN;
610     fid.Unique = vnode.uniquifier;
611
612     /*
613      * Now  update the parent directory.
614      */
615
616     ino = VNDISK_GET_INO(parent);
617     SetSalvageDirHandle(&dir, V_id(vol), V_device(vol), ino);
618
619     code = Delete(&dir, name);
620     if (code) {
621         Log("splitvolume: couldn't delete directory entry for %s in %u.%u.%u, code = %d\n",
622                         name, V_id(vol), vN, parent->uniquifier, code);
623         return code;
624     }
625     code = Create(&dir, name, &fid);
626     FidZap(&dir);
627
628     class = vLarge;
629     vcp = &VnodeClassInfo[class];
630     fdP = IH_OPEN(vol->vnodeIndex[class].handle);
631     offset = (vN + 1 - class) << (vcp->logSize -1);
632     parent->dataVersion++;
633     if (FDH_PWRITE(fdP, parent, vcp->diskSize, offset) != vcp->diskSize) {
634         Log("split volume: couldn't write vnode for parent directory %u.%u.%u\n",
635                 V_id(vol), vN, parent->uniquifier);
636         return EIO;
637     }
638     FDH_REALLYCLOSE(fdP);
639     return code;
640 }
641
642 static afs_int32
643 deleteVnodes(Volume *vol, afs_int32 class,
644              struct VnodeExtract *list, afs_int32 length,
645              afs_uint64 *blocks)
646 {
647     afs_int32 i, code = 0;
648     char buf[SIZEOF_LARGEDISKVNODE];
649     struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)&buf;
650     FdHandle_t *fdP = 0;
651     struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
652     struct VnodeExtract *e;
653     afs_sfsize_t size;
654     afs_uint64 offset;
655     Inode ino;
656
657     fdP = IH_OPEN(vol->vnodeIndex[class].handle);
658     if (!fdP) {
659         Log("Couldn't open %s Index of volume %u\n",
660                 class ? "small":"large", V_id(vol));
661         code = EIO;
662         goto Bad_Delete;
663     }
664
665     for (i=0; i<length; i++) {
666         e = &list[i];
667         if (e->flag & NEEDED) {
668             offset = (e->vN + 1 - class) << (vcp->logSize -1);
669             if (FDH_PREAD(fdP, vnode, vcp->diskSize, offset) != vcp->diskSize) {
670                 Log("Couldn't read in %s Index of volume %u at offset %"
671                     AFS_UINT64_FMT "\n", class ? "small":"large", V_id(vol),
672                     offset);
673                 code = EIO;
674                 goto Bad_Delete;
675             }
676             VNDISK_GET_LEN(size, vnode);
677             *blocks += (size + 0x3ff) >> 10;
678             ino = VNDISK_GET_INO(vnode);
679             if (ino) {
680                 IHandle_t *h;
681                 IH_INIT(h, vol->device, V_parentId(vol), ino);
682                     IH_DEC(h, ino, V_parentId(vol));
683 #ifdef AFS_RXOSD_SUPPORT
684             } else {
685                 code = osdRemove(vol, vnode, e->vN);
686 #endif /*  AFS_RXOSD_SUPPORT */
687             }
688             memset(vnode, 0, vcp->diskSize);
689             vnode->type = vNull;
690             if (FDH_PWRITE(fdP, vnode, vcp->diskSize, offset) != vcp->diskSize) {
691                    Log("Couldn't write in %s Index of volume %u to offset %"
692                        AFS_UINT64_FMT "\n", class ? "small":"large",
693                        V_id(vol), offset);
694             }
695         }
696     }
697 Bad_Delete:
698     if (fdP)
699         FDH_CLOSE(fdP);
700     return code;
701 }
702
703 afs_int32
704 split_volume(struct rx_call *call, Volume *vol, Volume *newvol,
705              afs_uint32 where, afs_int32 verbose)
706 {
707     Error code = 0;
708     struct VnodeExtract *dirList = 0;
709     struct VnodeExtract *fileList = 0;
710     afs_uint64 blocks = 0;
711     afs_uint32 filesNeeded, dirsNeeded;
712     afs_uint32 dl, fl;
713     char buf[SIZEOF_LARGEDISKVNODE];
714     char buf2[SIZEOF_LARGEDISKVNODE];
715     struct VnodeDiskObject *rootVnode = (struct VnodeDiskObject *)&buf;
716     struct VnodeDiskObject *parVnode = (struct VnodeDiskObject *)&buf2;
717     char name[256];
718     afs_uint32 parent;
719     struct Msg *m;
720
721     m = (struct Msg *) malloc(sizeof(struct Msg));
722     memset(m, 0, sizeof(struct Msg));
723     m->call = call;
724     m->verbose = verbose;
725
726     /*
727      *  First step: planning
728      *
729      *  Find out which directories will belong to the new volume
730      *
731      */
732     if (verbose) {
733         sprintf(m->line,
734                 "1st step: extract vnode essence from large vnode file\n");
735         rx_Write(m->call, m->line, strlen(m->line));
736     }
737
738     code = ExtractVnodes(m, vol, vLarge, &dirList, &dl, where, rootVnode,
739                         &parent, parVnode);
740     if (code) {
741         sprintf(m->line,
742                 "ExtractVnodes failed for %u for directories with code %d\n",
743                 V_id(vol), code);
744         rx_Write(m->call, m->line, strlen(m->line));
745         return code;
746     }
747
748     if (verbose) {
749         sprintf(m->line, "2nd step: look for name of vnode %u in directory %u.%u.%u\n",
750                 where, V_id(vol), parent, parVnode->uniquifier);
751         rx_Write(m->call, m->line, strlen(m->line));
752     }
753     code = findName(vol, parVnode, where, rootVnode->uniquifier,
754                     name,  sizeof(name));
755     if (code) {
756         sprintf(m->line,
757                 "splitvolume: could'nt find name of %u in directory %u.%u.%u.\n",
758                 where, V_id(vol), parent, parVnode->uniquifier);
759         rx_Write(m->call, m->line, strlen(m->line));
760         return code;
761     }
762     if (verbose) {
763         sprintf(m->line, "name of %u is %s\n", where, name);
764         rx_Write(m->call, m->line, strlen(m->line));
765     }
766
767     if (verbose) {
768         sprintf(m->line, "3rd step: find all directory vnodes belonging to the subtree under %u \"%s\"\n",
769                         where, name);
770         rx_Write(m->call, m->line, strlen(m->line));
771     }
772     code = FindVnodes(m, where, dirList, dl, dirList, dl, &dirsNeeded, 1);
773     if (code) {
774         sprintf(m->line,
775                 "FindVnodes for directories failed with code %d\n", code);
776         rx_Write(m->call, m->line, strlen(m->line));
777         return code;
778     }
779
780     if (verbose) {
781         sprintf(m->line, "4th step extract vnode essence from small vnode file\n");
782         rx_Write(m->call, m->line, strlen(m->line));
783     }
784     code = ExtractVnodes(m, vol, vSmall, &fileList, &fl, where, 0, 0, 0);
785     if (code) {
786         sprintf(m->line,
787                 "ExtractVnodes failed for %u for files with code %d\n",
788                 V_id(vol), code);
789         rx_Write(m->call, m->line, strlen(m->line));
790         return code;
791     }
792     if (verbose) {
793         sprintf(m->line, "5th step: find all small vnodes belonging to the subtree under %u \"%s\"\n",
794                         where, name);
795         rx_Write(m->call, m->line, strlen(m->line));
796     }
797     FindVnodes(m, where, fileList, fl, dirList, dl, &filesNeeded, 0);
798
799     /*
800      *  Third step: create hard links for all files needed
801      *
802      */
803
804     V_destroyMe(newvol) = DESTROY_ME;
805     V_inService(newvol) = 0;
806     if (verbose) {
807         sprintf(m->line, "6th step: create hard links in the AFSIDat tree between files of the old and new volume\n");
808         rx_Write(m->call, m->line, strlen(m->line));
809     }
810     code = copyVnodes(m, vol, newvol, 1, fileList, fl, where, &blocks, 0);
811     if (code) {
812         sprintf(m->line, "copyVnodes for files failed with code %d\n", code);
813         rx_Write(m->call, m->line, strlen(m->line));
814         return code;
815     }
816
817     /*
818      *  Forth step: create hard links for all directories and copy
819      *  split directory to new root directory
820      */
821
822     if (verbose) {
823         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",
824                 where);
825         rx_Write(m->call, m->line, strlen(m->line));
826     }
827     code = copyVnodes(m, vol, newvol, 0, dirList, dl, where, &blocks, parVnode);
828     if (code) {
829         sprintf(m->line, "copyVnodes for directories failed with code %d\n", code);
830         rx_Write(m->call, m->line, strlen(m->line));
831         return code;
832     }
833
834     /*
835      *  Finalize new volume
836      *
837      */
838     if (verbose) {
839         sprintf(m->line, "8th step: write new volume's metadata to disk\n");
840         rx_Write(m->call, m->line, strlen(m->line));
841     }
842
843     V_diskused(newvol) = blocks;
844 #ifdef AFS_RXOSD_SUPPORT
845     V_osdFlag(newvol) = V_osdFlag(vol);
846 #endif
847     V_filecount(newvol) = filesNeeded + dirsNeeded;
848     V_destroyMe(newvol) = 0;
849     V_maxquota(newvol) = V_maxquota(vol);
850     V_uniquifier(newvol) = V_uniquifier(vol);
851     V_inService(newvol) = 1;
852     VUpdateVolume(&code, newvol);
853
854     /*
855      *  Sixth step: change directory entry in old volume:
856      *  rename old tree and create mount point for new volume.
857      */
858     if (verbose) {
859         sprintf(m->line, "9th step: create mountpoint \"%s\" for new volume in old volume's directory %u.\n", name, parent);
860         rx_Write(m->call, m->line, strlen(m->line));
861     }
862
863     code = createMountpoint(vol, newvol, parVnode, parent, rootVnode, name);
864     if (code) {
865         sprintf(m->line, "createMountpoint failed with code %d\n", code);
866         rx_Write(m->call, m->line, strlen(m->line));
867         return code;
868     }
869     /*
870      * Now both volumes should be ready and consistent, but the old volume
871      * contains still the vnodes and data we transferred into the new one.
872      * Delete orphaned vnodes and data.
873      */
874
875     blocks = 0;
876     if (verbose) {
877         sprintf(m->line, "10th step: delete large vnodes belonging to subtree in the old volume.\n");
878         rx_Write(m->call, m->line, strlen(m->line));
879     }
880     deleteVnodes(vol, vLarge, dirList, dl, &blocks);
881     if (verbose) {
882         sprintf(m->line, "11th step: delete small vnodes belonging to subtree in the old volume.\n");
883         rx_Write(m->call, m->line, strlen(m->line));
884     }
885     deleteVnodes(vol, vSmall, fileList, fl, &blocks);
886     V_diskused(vol) -= blocks;
887     V_filecount(vol) -= (filesNeeded + dirsNeeded + 1);
888     VUpdateVolume(&code, vol);
889
890     sprintf(m->line, "Finished!\n");
891     rx_Write(m->call, m->line, strlen(m->line));
892     m->line[0] = 0;
893     m->line[1] = 0;
894     m->line[2] = 0;
895     m->line[3] = 0;
896     rx_Write(m->call, m->line, 4);
897     free(m);
898     return code;
899 }
900 #endif