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