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