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