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