7dbd3ec61b7d5752a45ed45592cf70421e29a2a2
[openafs.git] / src / vol / clone.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11         System:         VICE-TWO
12         Module:         clone.c
13
14  */
15
16 /* Clone a volume.  Assumes the new volume is already created */
17
18 #include <afsconfig.h>
19 #include <afs/param.h>
20
21
22 #include <sys/types.h>
23 #include <stdio.h>
24 #include <afs/afs_assert.h>
25 #ifdef AFS_NT40_ENV
26 #include <fcntl.h>
27 #include <windows.h>
28 #include <winbase.h>
29 #include <io.h>
30 #include <time.h>
31 #else
32 #include <sys/file.h>
33 #include <sys/time.h>
34 #include <unistd.h>
35 #endif
36 #include <string.h>
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_prototypes.h"
52 #include "common.h"
53
54 int (*vol_PollProc) (void) = 0; /* someone must init this */
55
56 #define ERROR_EXIT(code) {error = code; goto error_exit;}
57
58 /* parameters for idec call - this could just be an IHandle_t, but leaving
59  * open the possibility of decrementing the special files as well.
60  */
61 struct clone_rock {
62     IHandle_t *h;
63     VolId vol;
64 };
65
66 #define CLONE_MAXITEMS  100
67 struct clone_items {
68     struct clone_items *next;
69     afs_int32 nitems;
70     Inode data[CLONE_MAXITEMS];
71 };
72
73 struct clone_head {
74     struct clone_items *first;
75     struct clone_items *last;
76 };
77
78 void CloneVolume(Error *, Volume *, Volume *, Volume *);
79
80 static int
81 ci_AddItem(struct clone_head *ah, Inode aino)
82 {
83     struct clone_items *ti;
84
85     /* if no last elt (first call) or last item full, get a new one */
86     if ((!ah->last) || ah->last->nitems >= CLONE_MAXITEMS) {
87         ti = (struct clone_items *)malloc(sizeof(struct clone_items));
88         if (!ti) {
89             Log("ci_AddItem: malloc failed\n");
90             osi_Panic("ci_AddItem: malloc failed\n");
91         }
92         ti->nitems = 0;
93         ti->next = (struct clone_items *)0;
94         if (ah->last) {
95             ah->last->next = ti;
96             ah->last = ti;
97         } else {
98             /* first dude in the list */
99             ah->first = ah->last = ti;
100         }
101     } else
102         ti = ah->last;
103
104     /* now ti points to the end of the list, to a clone_item with room
105      * for at least one more element.  Add it.
106      */
107     ti->data[ti->nitems++] = aino;
108     return 0;
109 }
110
111 /* initialize a clone header */
112 int
113 ci_InitHead(struct clone_head *ah)
114 {
115     memset(ah, 0, sizeof(*ah));
116     return 0;
117 }
118
119 /* apply a function to all dudes in the set */
120 int
121 ci_Apply(struct clone_head *ah, int (*aproc) (Inode,  void *), void *arock)
122 {
123     struct clone_items *ti;
124     int i;
125
126     for (ti = ah->first; ti; ti = ti->next) {
127         for (i = 0; i < ti->nitems; i++) {
128             (*aproc) (ti->data[i], arock);
129         }
130     }
131     return 0;
132 }
133
134 /* free all dudes in the list */
135 int
136 ci_Destroy(struct clone_head *ah)
137 {
138     struct clone_items *ti, *ni;
139
140     for (ti = ah->first; ti; ti = ni) {
141         ni = ti->next;          /* guard against freeing */
142         free(ti);
143     }
144     return 0;
145 }
146
147 static int
148 IDecProc(Inode adata, void *arock)
149 {
150     struct clone_rock *aparm = (struct clone_rock *)arock;
151     IH_DEC(aparm->h, adata, aparm->vol);
152     DOPOLL;
153     return 0;
154 }
155
156 afs_int32
157 DoCloneIndex(Volume * rwvp, Volume * clvp, VnodeClass class, int reclone)
158 {
159     afs_int32 code, error = 0;
160     FdHandle_t *rwFd = 0, *clFdIn = 0, *clFdOut = 0;
161     StreamHandle_t *rwfile = 0, *clfilein = 0, *clfileout = 0;
162     IHandle_t *rwH = 0, *clHin = 0, *clHout = 0;
163     char buf[SIZEOF_LARGEDISKVNODE], dbuf[SIZEOF_LARGEDISKVNODE];
164     struct VnodeDiskObject *rwvnode = (struct VnodeDiskObject *)buf;
165     struct VnodeDiskObject *clvnode = (struct VnodeDiskObject *)dbuf;
166     Inode rwinode = 0;
167     Inode clinode;
168     struct clone_head decHead;
169     struct clone_rock decRock;
170     afs_int32 offset = 0;
171     afs_int32 dircloned, inodeinced;
172     afs_int32 filecount = 0, diskused = 0;
173     afs_ino_str_t stmp;
174
175     struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
176     int ReadWriteOriginal = VolumeWriteable(rwvp);
177
178     /* Correct number of files in volume: this assumes indexes are always
179        cloned starting with vLarge */
180     if (ReadWriteOriginal && class != vLarge) {
181         filecount = V_filecount(rwvp);
182         diskused = V_diskused(rwvp);
183     }
184
185     /* Open the RW volume's index file and seek to beginning */
186     IH_COPY(rwH, rwvp->vnodeIndex[class].handle);
187     rwFd = IH_OPEN(rwH);
188     if (!rwFd)
189         ERROR_EXIT(EIO);
190     rwfile = FDH_FDOPEN(rwFd, ReadWriteOriginal ? "r+" : "r");
191     if (!rwfile)
192         ERROR_EXIT(EIO);
193     STREAM_ASEEK(rwfile, vcp->diskSize);        /* Will fail if no vnodes */
194
195     /* Open the clone volume's index file and seek to beginning */
196     IH_COPY(clHout, clvp->vnodeIndex[class].handle);
197     clFdOut = IH_OPEN(clHout);
198     if (!clFdOut)
199         ERROR_EXIT(EIO);
200     clfileout = FDH_FDOPEN(clFdOut, "a");
201     if (!clfileout)
202         ERROR_EXIT(EIO);
203     code = STREAM_ASEEK(clfileout, vcp->diskSize);
204     if (code)
205         ERROR_EXIT(EIO);
206
207     /* If recloning, open the new volume's index; this time for
208      * reading. We never read anything that we're simultaneously
209      * writing, so this all works.
210      */
211     if (reclone) {
212         IH_COPY(clHin, clvp->vnodeIndex[class].handle);
213         clFdIn = IH_OPEN(clHin);
214         if (!clFdIn)
215             ERROR_EXIT(EIO);
216         clfilein = FDH_FDOPEN(clFdIn, "r");
217         if (!clfilein)
218             ERROR_EXIT(EIO);
219         STREAM_ASEEK(clfilein, vcp->diskSize);  /* Will fail if no vnodes */
220     }
221
222     /* Initialize list of inodes to nuke */
223     ci_InitHead(&decHead);
224     decRock.h = V_linkHandle(rwvp);
225     decRock.vol = V_parentId(rwvp);
226
227     /* Read each vnode in the old volume's index file */
228     for (offset = vcp->diskSize;
229          STREAM_READ(rwvnode, vcp->diskSize, 1, rwfile) == 1;
230          offset += vcp->diskSize) {
231         dircloned = inodeinced = 0;
232
233         /* If we are recloning the volume, read the corresponding vnode
234          * from the clone and determine its inode number.
235          */
236         if (reclone && !STREAM_EOF(clfilein)
237             && (STREAM_READ(clvnode, vcp->diskSize, 1, clfilein) == 1)) {
238             clinode = VNDISK_GET_INO(clvnode);
239         } else {
240             clinode = 0;
241         }
242
243         if (rwvnode->type != vNull) {
244             afs_fsize_t ll;
245
246             if (rwvnode->vnodeMagic != vcp->magic)
247                 ERROR_EXIT(-1);
248             rwinode = VNDISK_GET_INO(rwvnode);
249             filecount++;
250             VNDISK_GET_LEN(ll, rwvnode);
251             diskused += nBlocks(ll);
252
253             /* Increment the inode if not already */
254             if (clinode && (clinode == rwinode)) {
255                 clinode = 0;    /* already cloned - don't delete later */
256             } else if (rwinode) {
257                 if (IH_INC(V_linkHandle(rwvp), rwinode, V_parentId(rwvp)) ==
258                     -1) {
259                     Log("IH_INC failed: %"AFS_PTR_FMT", %s, %u errno %d\n",
260                         V_linkHandle(rwvp), PrintInode(stmp, rwinode),
261                         V_parentId(rwvp), errno);
262                     VForceOffline(rwvp);
263                     ERROR_EXIT(EIO);
264                 }
265                 inodeinced = 1;
266             }
267
268             /* If a directory, mark vnode in old volume as cloned */
269             if ((rwvnode->type == vDirectory) && ReadWriteOriginal) {
270 #ifdef DVINC
271                 /*
272                  * It is my firmly held belief that immediately after
273                  * copy-on-write, the two directories can be identical.
274                  * If the new copy is changed (presumably, that is the very
275                  * next thing that will happen) then the dataVersion will
276                  * get bumped.
277                  */
278                 /* NOTE:  the dataVersion++ is incredibly important!!!.
279                  * This will cause the inode created by the file server
280                  * on copy-on-write to be stamped with a dataVersion bigger
281                  * than the current one.  The salvager will then do the
282                  * right thing */
283                 rwvnode->dataVersion++;
284 #endif /* DVINC */
285                 rwvnode->cloned = 1;
286                 code = STREAM_ASEEK(rwfile, offset);
287                 if (code == -1)
288                     goto clonefailed;
289                 code = STREAM_WRITE(rwvnode, vcp->diskSize, 1, rwfile);
290                 if (code != 1)
291                     goto clonefailed;
292                 dircloned = 1;
293                 code = STREAM_ASEEK(rwfile, offset + vcp->diskSize);
294                 if (code == -1)
295                     goto clonefailed;
296 #ifdef DVINC
297                 rwvnode->dataVersion--; /* Really needs to be set to the value in the inode,
298                                          * for the read-only volume */
299 #endif /* DVINC */
300             }
301         }
302
303         /* Overwrite the vnode entry in the clone volume */
304         rwvnode->cloned = 0;
305         code = STREAM_WRITE(rwvnode, vcp->diskSize, 1, clfileout);
306         if (code != 1) {
307           clonefailed:
308             /* Couldn't clone, go back and decrement the inode's link count */
309             if (inodeinced) {
310                 if (IH_DEC(V_linkHandle(rwvp), rwinode, V_parentId(rwvp)) ==
311                     -1) {
312                     Log("IH_DEC failed: %"AFS_PTR_FMT", %s, %u errno %d\n",
313                         V_linkHandle(rwvp), PrintInode(stmp, rwinode),
314                         V_parentId(rwvp), errno);
315                     VForceOffline(rwvp);
316                     ERROR_EXIT(EIO);
317                 }
318             }
319             /* And if the directory was marked clone, unmark it */
320             if (dircloned) {
321                 rwvnode->cloned = 0;
322                 if (STREAM_ASEEK(rwfile, offset) != -1)
323                     (void)STREAM_WRITE(rwvnode, vcp->diskSize, 1, rwfile);
324             }
325             ERROR_EXIT(EIO);
326         }
327
328         /* Removal of the old cloned inode */
329         if (clinode) {
330             ci_AddItem(&decHead, clinode);      /* just queue it */
331         }
332
333         DOPOLL;
334     }
335     if (STREAM_ERROR(clfileout))
336         ERROR_EXIT(EIO);
337
338     /* Clean out any junk at end of clone file */
339     if (reclone) {
340         STREAM_ASEEK(clfilein, offset);
341         while (STREAM_READ(clvnode, vcp->diskSize, 1, clfilein) == 1) {
342             if (clvnode->type != vNull && VNDISK_GET_INO(clvnode) != 0) {
343                 ci_AddItem(&decHead, VNDISK_GET_INO(clvnode));
344             }
345             DOPOLL;
346         }
347     }
348
349     /* come here to finish up.  If code is non-zero, we've already run into problems,
350      * and shouldn't do the idecs.
351      */
352   error_exit:
353     if (rwfile)
354         STREAM_CLOSE(rwfile);
355     if (clfilein)
356         STREAM_CLOSE(clfilein);
357     if (clfileout)
358         STREAM_CLOSE(clfileout);
359
360     if (rwFd)
361         FDH_CLOSE(rwFd);
362     if (clFdIn)
363         FDH_CLOSE(clFdIn);
364     if (clFdOut)
365         FDH_CLOSE(clFdOut);
366
367     if (rwH)
368         IH_RELEASE(rwH);
369     if (clHout)
370         IH_RELEASE(clHout);
371     if (clHin)
372         IH_RELEASE(clHin);
373
374     /* Next, we sync the disk. We have to reopen in case we're truncating,
375      * since we were using stdio above, and don't know when the buffers
376      * would otherwise be flushed.  There's no stdio fftruncate call.
377      */
378     rwFd = IH_OPEN(clvp->vnodeIndex[class].handle);
379     if (rwFd == NULL) {
380         if (!error)
381             error = EIO;
382     } else {
383         if (reclone) {
384             /* If doing a reclone, we're keeping the clone. We need to
385              * truncate the file to offset bytes.
386              */
387             if (reclone && !error) {
388                 error = FDH_TRUNC(rwFd, offset);
389             }
390         }
391         FDH_SYNC(rwFd);
392         FDH_CLOSE(rwFd);
393     }
394
395     /* Now finally do the idec's.  At this point, all potential
396      * references have been cleaned up and sent to the disk
397      * (see above fclose and fsync). No matter what happens, we
398      * no longer need to keep these references around.
399      */
400     code = ci_Apply(&decHead, IDecProc, (char *)&decRock);
401     if (!error)
402         error = code;
403     ci_Destroy(&decHead);
404
405     if (ReadWriteOriginal && filecount > 0)
406         V_filecount(rwvp) = filecount;
407     if (ReadWriteOriginal && diskused > 0)
408         V_diskused(rwvp) = diskused;
409     return error;
410 }
411
412 void
413 CloneVolume(Error * rerror, Volume * original, Volume * new, Volume * old)
414 {
415     afs_int32 code, error = 0;
416     afs_int32 reclone;
417     afs_int32 filecount = V_filecount(original), diskused = V_diskused(original);
418
419     *rerror = 0;
420     reclone = ((new == old) ? 1 : 0);
421
422     code = DoCloneIndex(original, new, vLarge, reclone);
423     if (code)
424         ERROR_EXIT(code);
425     code = DoCloneIndex(original, new, vSmall, reclone);
426     if (code)
427         ERROR_EXIT(code);
428     if (filecount != V_filecount(original) || diskused != V_diskused(original))
429        Log("Clone %u: filecount %d -> %d diskused %d -> %d\n",
430             V_id(original), filecount, V_filecount(original), diskused, V_diskused(original));
431
432     code = CopyVolumeHeader(&V_disk(original), &V_disk(new));
433     if (code)
434         ERROR_EXIT(code);
435
436   error_exit:
437     *rerror = error;
438 }