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