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