2981606653473cf40ffc04554b94070e49eb527a
[openafs.git] / src / vol / vutil.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:         vutil.c
13         Institution:    The Information Technology Center, Carnegie-Mellon University
14
15  */
16
17 #include <afsconfig.h>
18 #include <afs/param.h>
19
20
21 #include <stdio.h>
22 #include <sys/types.h>
23 #include <errno.h>
24 #ifdef AFS_NT40_ENV
25 #include <time.h>
26 #include <fcntl.h>
27 #else
28 #include <sys/time.h>
29 #include <sys/file.h>
30 #include <unistd.h>
31 #endif
32 #include <sys/stat.h>
33 #ifdef AFS_PTHREAD_ENV
34 #include <assert.h>
35 #else /* AFS_PTHREAD_ENV */
36 #include <afs/assert.h>
37 #endif /* AFS_PTHREAD_ENV */
38
39 #include <rx/xdr.h>
40 #include <afs/afsint.h>
41 #include "nfs.h"
42 #include <afs/errors.h>
43 #include "lock.h"
44 #include "lwp.h"
45 #include <afs/afssyscalls.h>
46 #include "ihandle.h"
47 #include <afs/afsutil.h>
48 #ifdef AFS_NT40_ENV
49 #include "ntops.h"
50 #include <io.h>
51 #endif
52 #include "vnode.h"
53 #include "volume.h"
54 #include "partition.h"
55 #include "viceinode.h"
56
57 #include "volinodes.h"
58 #ifdef  AFS_AIX_ENV
59 #include <sys/lockf.h>
60 #endif
61 #if defined(AFS_SUN5_ENV) || defined(AFS_NT40_ENV) || defined(AFS_LINUX20_ENV)
62 #include <string.h>
63 #else
64 #include <strings.h>
65 #endif
66
67 #ifdef O_LARGEFILE
68 #define afs_open        open64
69 #else /* !O_LARGEFILE */
70 #define afs_open        open
71 #endif /* !O_LARGEFILE */
72
73 /*@printflike@*/ extern void Log(const char *format, ...);
74
75 void AssignVolumeName(register VolumeDiskData * vol, char *name, char *ext);
76 void AssignVolumeName_r(register VolumeDiskData * vol, char *name, char *ext);
77 void ClearVolumeStats(register VolumeDiskData * vol);
78 void ClearVolumeStats_r(register VolumeDiskData * vol);
79
80
81 #define nFILES  (sizeof (stuff)/sizeof(struct stuff))
82
83 /* Note:  the volume creation functions herein leave the destroyMe flag in the
84    volume header ON:  this means that the volumes will not be attached by the
85    file server and WILL BE DESTROYED the next time a system salvage is performed */
86
87 static void
88 RemoveInodes(Device dev, VolumeId vid)
89 {
90     register int i;
91     IHandle_t *handle;
92
93     /* This relies on the fact that IDEC only needs the device and NT only
94      * needs the dev and vid to decrement volume special files.
95      */
96     IH_INIT(handle, dev, vid, -1);
97     for (i = 0; i < nFILES; i++) {
98         Inode inode = *stuff[i].inode;
99         if (VALID_INO(inode))
100             IH_DEC(handle, inode, vid);
101     }
102     IH_RELEASE(handle);
103 }
104
105 Volume *
106 VCreateVolume(Error * ec, char *partname, VolId volumeId, VolId parentId)
107 {                               /* Should be the same as volumeId if there is
108                                  * no parent */
109     Volume *retVal;
110     VOL_LOCK;
111     retVal = VCreateVolume_r(ec, partname, volumeId, parentId);
112     VOL_UNLOCK;
113     return retVal;
114 }
115
116 Volume *
117 VCreateVolume_r(Error * ec, char *partname, VolId volumeId, VolId parentId)
118 {                               /* Should be the same as volumeId if there is
119                                  * no parent */
120     VolumeDiskData vol;
121     int fd, i;
122     char headerName[32], volumePath[64];
123     Device device;
124     struct DiskPartition64 *partition;
125     struct VolumeDiskHeader diskHeader;
126     IHandle_t *handle;
127     FdHandle_t *fdP;
128     Inode nearInode = 0;
129     char *part, *name;
130
131     *ec = 0;
132     memset(&vol, 0, sizeof(vol));
133     vol.id = volumeId;
134     vol.parentId = parentId;
135     vol.copyDate = time(0);     /* The only date which really means when this
136                                  * @i(instance) of this volume was created.
137                                  * Creation date does not mean this */
138
139     /* Initialize handle for error case below. */
140     handle = NULL;
141
142     /* Verify that the parition is valid before writing to it. */
143     if (!(partition = VGetPartition_r(partname, 0))) {
144         Log("VCreateVolume: partition %s is not in service.\n", partname);
145         *ec = VNOVOL;
146         return NULL;
147     }
148 #if     defined(NEARINODE_HINT)
149     nearInodeHash(volumeId, nearInode);
150     nearInode %= partition->f_files;
151 #endif
152     VGetVolumePath(ec, vol.id, &part, &name);
153     if (*ec == VNOVOL || !strcmp(partition->name, part)) {
154         /* this case is ok */
155     } else {
156         /* return EXDEV if it's a clone to an alternate partition
157          * otherwise assume it's a move */
158         if (vol.parentId != vol.id) {
159             *ec = EXDEV;
160             return NULL;
161         }
162     }
163     VLockPartition_r(partname);
164     memset(&tempHeader, 0, sizeof(tempHeader));
165     tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
166     tempHeader.stamp.version = VOLUMEHEADERVERSION;
167     tempHeader.id = vol.id;
168     tempHeader.parent = vol.parentId;
169     vol.stamp.magic = VOLUMEINFOMAGIC;
170     vol.stamp.version = VOLUMEINFOVERSION;
171     vol.destroyMe = DESTROY_ME;
172     (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_cast_uint32(vol.id));
173     (void)afs_snprintf(volumePath, sizeof volumePath, "%s/%s",
174                        VPartitionPath(partition), headerName);
175     fd = afs_open(volumePath, O_CREAT | O_EXCL | O_WRONLY, 0600);
176     if (fd == -1) {
177         if (errno == EEXIST) {
178             Log("VCreateVolume: Header file %s already exists!\n",
179                 volumePath);
180             *ec = VVOLEXISTS;
181         } else {
182             Log("VCreateVolume: Couldn't create header file %s for volume %u\n", volumePath, vol.id);
183             *ec = VNOVOL;
184         }
185         return NULL;
186     }
187     device = partition->device;
188
189     for (i = 0; i < nFILES; i++) {
190         register struct stuff *p = &stuff[i];
191         if (p->obsolete)
192             continue;
193 #ifdef AFS_NAMEI_ENV
194         *(p->inode) =
195             IH_CREATE(NULL, device, VPartitionPath(partition), nearInode,
196                       (p->inodeType == VI_LINKTABLE) ? vol.parentId : vol.id,
197                       INODESPECIAL, p->inodeType, vol.parentId);
198         if (!(VALID_INO(*(p->inode)))) {
199             if (errno == EEXIST) {
200                 /* Increment the reference count instead. */
201                 IHandle_t *lh;
202                 int code;
203
204 #ifdef AFS_NT40_ENV
205                 *(p->inode) = nt_MakeSpecIno(VI_LINKTABLE);
206 #else
207                 *(p->inode) = namei_MakeSpecIno(vol.parentId, VI_LINKTABLE);
208 #endif
209                 IH_INIT(lh, device, parentId, *(p->inode));
210                 fdP = IH_OPEN(lh);
211                 if (fdP == NULL) {
212                     IH_RELEASE(lh);
213                     goto bad;
214                 }
215                 code = IH_INC(lh, *(p->inode), parentId);
216                 FDH_REALLYCLOSE(fdP);
217                 IH_RELEASE(lh);
218                 if (code < 0)
219                     goto bad;
220                 continue;
221             }
222         }
223 #else
224         *(p->inode) =
225             IH_CREATE(NULL, device, VPartitionPath(partition), nearInode,
226                       vol.id, INODESPECIAL, p->inodeType, vol.parentId);
227 #endif
228
229         if (!VALID_INO(*(p->inode))) {
230             Log("VCreateVolume:  Problem creating %s file associated with volume header %s\n", p->description, volumePath);
231           bad:
232             if (handle)
233                 IH_RELEASE(handle);
234             RemoveInodes(device, vol.id);
235             *ec = VNOVOL;
236             close(fd);
237             return NULL;
238         }
239         IH_INIT(handle, device, vol.parentId, *(p->inode));
240         fdP = IH_OPEN(handle);
241         if (fdP == NULL) {
242             Log("VCreateVolume:  Problem iopen inode %s (err=%d)\n",
243                 PrintInode(NULL, *(p->inode)), errno);
244             goto bad;
245         }
246         if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
247             Log("VCreateVolume:  Problem lseek inode %s (err=%d)\n",
248                 PrintInode(NULL, *(p->inode)), errno);
249             FDH_REALLYCLOSE(fdP);
250             goto bad;
251         }
252         if (FDH_WRITE(fdP, (char *)&p->stamp, sizeof(p->stamp)) !=
253             sizeof(p->stamp)) {
254             Log("VCreateVolume:  Problem writing to  inode %s (err=%d)\n",
255                 PrintInode(NULL, *(p->inode)), errno);
256             FDH_REALLYCLOSE(fdP);
257             goto bad;
258         }
259         FDH_REALLYCLOSE(fdP);
260         IH_RELEASE(handle);
261         nearInode = *(p->inode);
262     }
263
264     IH_INIT(handle, device, vol.parentId, tempHeader.volumeInfo);
265     fdP = IH_OPEN(handle);
266     if (fdP == NULL) {
267         Log("VCreateVolume:  Problem iopen inode %s (err=%d)\n",
268             PrintInode(NULL, tempHeader.volumeInfo), errno);
269         unlink(volumePath);
270         goto bad;
271     }
272     if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
273         Log("VCreateVolume:  Problem lseek inode %s (err=%d)\n",
274             PrintInode(NULL, tempHeader.volumeInfo), errno);
275         FDH_REALLYCLOSE(fdP);
276         unlink(volumePath);
277         goto bad;
278     }
279     if (FDH_WRITE(fdP, (char *)&vol, sizeof(vol)) != sizeof(vol)) {
280         Log("VCreateVolume:  Problem writing to  inode %s (err=%d)\n",
281             PrintInode(NULL, tempHeader.volumeInfo), errno);
282         FDH_REALLYCLOSE(fdP);
283         unlink(volumePath);
284         goto bad;
285     }
286     FDH_CLOSE(fdP);
287     IH_RELEASE(handle);
288
289     VolumeHeaderToDisk(&diskHeader, &tempHeader);
290     if (write(fd, &diskHeader, sizeof(diskHeader)) != sizeof(diskHeader)) {
291         Log("VCreateVolume: Unable to write volume header %s; volume %u not created\n", volumePath, vol.id);
292         unlink(volumePath);
293         goto bad;
294     }
295     fsync(fd);
296     close(fd);
297     return (VAttachVolumeByName_r(ec, partname, headerName, V_SECRETLY));
298 }
299
300
301 void
302 AssignVolumeName(register VolumeDiskData * vol, char *name, char *ext)
303 {
304     VOL_LOCK;
305     AssignVolumeName_r(vol, name, ext);
306     VOL_UNLOCK;
307 }
308
309 void
310 AssignVolumeName_r(register VolumeDiskData * vol, char *name, char *ext)
311 {
312     register char *dot;
313     strncpy(vol->name, name, VNAMESIZE - 1);
314     vol->name[VNAMESIZE - 1] = '\0';
315     dot = strrchr(vol->name, '.');
316     if (dot && (strcmp(dot, ".backup") == 0 || strcmp(dot, ".readonly") == 0))
317         *dot = 0;
318     if (ext)
319         strncat(vol->name, ext, VNAMESIZE - 1 - strlen(vol->name));
320 }
321
322 afs_int32
323 CopyVolumeHeader_r(VolumeDiskData * from, VolumeDiskData * to)
324 {
325     /* The id and parentId fields are not copied; these are inviolate--the to volume
326      * is assumed to have already been created.  The id's cannot be changed once
327      * creation has taken place, since they are embedded in the various inodes associated
328      * with the volume.  The copydate is also inviolate--it always reflects the time
329      * this volume was created (compare with the creation date--the creation date of
330      * a backup volume is the creation date of the original parent, because the backup
331      * is used to backup the parent volume). */
332     Date copydate;
333     VolumeId id, parent;
334     id = to->id;
335     parent = to->parentId;
336     copydate = to->copyDate;
337     memcpy(to, from, sizeof(*from));
338     to->id = id;
339     to->parentId = parent;
340     to->copyDate = copydate;
341     to->destroyMe = DESTROY_ME; /* Caller must always clear this!!! */
342     to->stamp.magic = VOLUMEINFOMAGIC;
343     to->stamp.version = VOLUMEINFOVERSION;
344     return 0;
345 }
346
347 afs_int32
348 CopyVolumeHeader(VolumeDiskData * from, VolumeDiskData * to)
349 {
350     afs_int32 code;
351
352     VOL_LOCK;
353     code = CopyVolumeHeader_r(from, to);
354     VOL_UNLOCK;
355     return (code);
356 }
357
358 void
359 ClearVolumeStats(register VolumeDiskData * vol)
360 {
361     VOL_LOCK;
362     ClearVolumeStats_r(vol);
363     VOL_UNLOCK;
364 }
365
366 void
367 ClearVolumeStats_r(register VolumeDiskData * vol)
368 {
369     memset(vol->weekUse, 0, sizeof(vol->weekUse));
370     vol->dayUse = 0;
371     vol->dayUseDate = 0;
372 }