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