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