Consolidate code for reading/writing vol headers
[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 #include "vol_prototypes.h"
59
60 #ifdef  AFS_AIX_ENV
61 #include <sys/lockf.h>
62 #endif
63 #if defined(AFS_SUN5_ENV) || defined(AFS_NT40_ENV) || defined(AFS_LINUX20_ENV)
64 #include <string.h>
65 #else
66 #include <strings.h>
67 #endif
68
69 #ifdef O_LARGEFILE
70 #define afs_open        open64
71 #else /* !O_LARGEFILE */
72 #define afs_open        open
73 #endif /* !O_LARGEFILE */
74
75 /*@printflike@*/ extern void Log(const char *format, ...);
76
77 #define nFILES  (sizeof (stuff)/sizeof(struct stuff))
78
79 /* Note:  the volume creation functions herein leave the destroyMe flag in the
80    volume header ON:  this means that the volumes will not be attached by the
81    file server and WILL BE DESTROYED the next time a system salvage is performed */
82
83 static void
84 RemoveInodes(Device dev, VolumeId vid)
85 {
86     register int i;
87     IHandle_t *handle;
88
89     /* This relies on the fact that IDEC only needs the device and NT only
90      * needs the dev and vid to decrement volume special files.
91      */
92     IH_INIT(handle, dev, vid, -1);
93     for (i = 0; i < nFILES; i++) {
94         Inode inode = *stuff[i].inode;
95         if (VALID_INO(inode))
96             IH_DEC(handle, inode, vid);
97     }
98     IH_RELEASE(handle);
99 }
100
101 Volume *
102 VCreateVolume(Error * ec, char *partname, VolId volumeId, VolId parentId)
103 {                               /* Should be the same as volumeId if there is
104                                  * no parent */
105     Volume *retVal;
106     VOL_LOCK;
107     retVal = VCreateVolume_r(ec, partname, volumeId, parentId);
108     VOL_UNLOCK;
109     return retVal;
110 }
111
112 Volume *
113 VCreateVolume_r(Error * ec, char *partname, VolId volumeId, VolId parentId)
114 {                               /* Should be the same as volumeId if there is
115                                  * no parent */
116     VolumeDiskData vol;
117     int i, rc;
118     char headerName[VMAXPATHLEN], volumePath[VMAXPATHLEN];
119     Device device;
120     struct DiskPartition64 *partition;
121     struct VolumeDiskHeader diskHeader;
122     IHandle_t *handle;
123     FdHandle_t *fdP;
124     Inode nearInode = 0;
125     char *part, *name;
126     struct stat st;
127
128     *ec = 0;
129     memset(&vol, 0, sizeof(vol));
130     vol.id = volumeId;
131     vol.parentId = parentId;
132     vol.copyDate = time(0);     /* The only date which really means when this
133                                  * @i(instance) of this volume was created.
134                                  * Creation date does not mean this */
135
136     /* Initialize handle for error case below. */
137     handle = NULL;
138
139     /* Verify that the parition is valid before writing to it. */
140     if (!(partition = VGetPartition_r(partname, 0))) {
141         Log("VCreateVolume: partition %s is not in service.\n", partname);
142         *ec = VNOVOL;
143         return NULL;
144     }
145 #if     defined(NEARINODE_HINT)
146     nearInodeHash(volumeId, nearInode);
147     nearInode %= partition->f_files;
148 #endif
149     VGetVolumePath(ec, vol.id, &part, &name);
150     if (*ec == VNOVOL || !strcmp(partition->name, part)) {
151         /* this case is ok */
152     } else {
153         /* return EXDEV if it's a clone to an alternate partition
154          * otherwise assume it's a move */
155         if (vol.parentId != vol.id) {
156             *ec = EXDEV;
157             return NULL;
158         }
159     }
160     *ec = 0;
161     VLockPartition_r(partname);
162     memset(&tempHeader, 0, sizeof(tempHeader));
163     tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
164     tempHeader.stamp.version = VOLUMEHEADERVERSION;
165     tempHeader.id = vol.id;
166     tempHeader.parent = vol.parentId;
167     vol.stamp.magic = VOLUMEINFOMAGIC;
168     vol.stamp.version = VOLUMEINFOVERSION;
169     vol.destroyMe = DESTROY_ME;
170     (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(vol.id));
171     (void)afs_snprintf(volumePath, sizeof volumePath, "%s/%s",
172                        VPartitionPath(partition), headerName);
173     rc = stat(volumePath, &st);
174     if (rc == 0 || errno != ENOENT) {
175         if (rc == 0) {
176             Log("VCreateVolume: Header file %s already exists!\n",
177                 volumePath);
178             *ec = VVOLEXISTS;
179         } else {
180             Log("VCreateVolume: Error %d trying to stat header file %s\n",
181                 errno, volumePath);
182             *ec = VNOVOL;
183         }
184         return NULL;
185     }
186     device = partition->device;
187
188     for (i = 0; i < nFILES; i++) {
189         register struct stuff *p = &stuff[i];
190         if (p->obsolete)
191             continue;
192 #ifdef AFS_NAMEI_ENV
193         *(p->inode) =
194             IH_CREATE(NULL, device, VPartitionPath(partition), nearInode,
195                       (p->inodeType == VI_LINKTABLE) ? vol.parentId : vol.id,
196                       INODESPECIAL, p->inodeType, vol.parentId);
197         if (!(VALID_INO(*(p->inode)))) {
198             if (errno == EEXIST) {
199                 /* Increment the reference count instead. */
200                 IHandle_t *lh;
201                 int code;
202
203 #ifdef AFS_NT40_ENV
204                 *(p->inode) = nt_MakeSpecIno(VI_LINKTABLE);
205 #else
206                 *(p->inode) = namei_MakeSpecIno(vol.parentId, VI_LINKTABLE);
207 #endif
208                 IH_INIT(lh, device, parentId, *(p->inode));
209                 fdP = IH_OPEN(lh);
210                 if (fdP == NULL) {
211                     IH_RELEASE(lh);
212                     goto bad;
213                 }
214                 code = IH_INC(lh, *(p->inode), parentId);
215                 FDH_REALLYCLOSE(fdP);
216                 IH_RELEASE(lh);
217                 if (code < 0)
218                     goto bad;
219                 continue;
220             }
221         }
222 #else
223         *(p->inode) =
224             IH_CREATE(NULL, device, VPartitionPath(partition), nearInode,
225                       vol.id, INODESPECIAL, p->inodeType, vol.parentId);
226 #endif
227
228         if (!VALID_INO(*(p->inode))) {
229             Log("VCreateVolume:  Problem creating %s file associated with volume header %s\n", p->description, volumePath);
230           bad:
231             if (handle)
232                 IH_RELEASE(handle);
233             RemoveInodes(device, vol.id);
234             if (!*ec) {
235                 *ec = VNOVOL;
236             }
237             VDestroyVolumeDiskHeader(partition, volumeId, parentId);
238             return NULL;
239         }
240         IH_INIT(handle, device, vol.parentId, *(p->inode));
241         fdP = IH_OPEN(handle);
242         if (fdP == NULL) {
243             Log("VCreateVolume:  Problem iopen inode %s (err=%d)\n",
244                 PrintInode(NULL, *(p->inode)), errno);
245             goto bad;
246         }
247         if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
248             Log("VCreateVolume:  Problem lseek inode %s (err=%d)\n",
249                 PrintInode(NULL, *(p->inode)), errno);
250             FDH_REALLYCLOSE(fdP);
251             goto bad;
252         }
253         if (FDH_WRITE(fdP, (char *)&p->stamp, sizeof(p->stamp)) !=
254             sizeof(p->stamp)) {
255             Log("VCreateVolume:  Problem writing to  inode %s (err=%d)\n",
256                 PrintInode(NULL, *(p->inode)), errno);
257             FDH_REALLYCLOSE(fdP);
258             goto bad;
259         }
260         FDH_REALLYCLOSE(fdP);
261         IH_RELEASE(handle);
262         nearInode = *(p->inode);
263     }
264
265     IH_INIT(handle, device, vol.parentId, tempHeader.volumeInfo);
266     fdP = IH_OPEN(handle);
267     if (fdP == NULL) {
268         Log("VCreateVolume:  Problem iopen inode %s (err=%d)\n",
269             PrintInode(NULL, tempHeader.volumeInfo), errno);
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         goto bad;
277     }
278     if (FDH_WRITE(fdP, (char *)&vol, sizeof(vol)) != sizeof(vol)) {
279         Log("VCreateVolume:  Problem writing to  inode %s (err=%d)\n",
280             PrintInode(NULL, tempHeader.volumeInfo), errno);
281         FDH_REALLYCLOSE(fdP);
282         goto bad;
283     }
284     FDH_CLOSE(fdP);
285     IH_RELEASE(handle);
286
287     VolumeHeaderToDisk(&diskHeader, &tempHeader);
288     rc = VCreateVolumeDiskHeader(&diskHeader, partition);
289     if (rc) {
290         Log("VCreateVolume: Error %d trying to write volume header for "
291             "volume %u on partition %s; volume not created\n", rc,
292             vol.id, VPartitionPath(partition));
293         if (rc == EEXIST) {
294             *ec = VVOLEXISTS;
295         }
296         goto bad;
297     }
298
299     return (VAttachVolumeByName_r(ec, partname, headerName, V_SECRETLY));
300 }
301
302
303 void
304 AssignVolumeName(register VolumeDiskData * vol, char *name, char *ext)
305 {
306     VOL_LOCK;
307     AssignVolumeName_r(vol, name, ext);
308     VOL_UNLOCK;
309 }
310
311 void
312 AssignVolumeName_r(register VolumeDiskData * vol, char *name, char *ext)
313 {
314     register char *dot;
315     strncpy(vol->name, name, VNAMESIZE - 1);
316     vol->name[VNAMESIZE - 1] = '\0';
317     dot = strrchr(vol->name, '.');
318     if (dot && (strcmp(dot, ".backup") == 0 || strcmp(dot, ".readonly") == 0))
319         *dot = 0;
320     if (ext)
321         strncat(vol->name, ext, VNAMESIZE - 1 - strlen(vol->name));
322 }
323
324 afs_int32
325 CopyVolumeHeader_r(VolumeDiskData * from, VolumeDiskData * to)
326 {
327     /* The id and parentId fields are not copied; these are inviolate--the to volume
328      * is assumed to have already been created.  The id's cannot be changed once
329      * creation has taken place, since they are embedded in the various inodes associated
330      * with the volume.  The copydate is also inviolate--it always reflects the time
331      * this volume was created (compare with the creation date--the creation date of
332      * a backup volume is the creation date of the original parent, because the backup
333      * is used to backup the parent volume). */
334     Date copydate;
335     VolumeId id, parent;
336     id = to->id;
337     parent = to->parentId;
338     copydate = to->copyDate;
339     memcpy(to, from, sizeof(*from));
340     to->id = id;
341     to->parentId = parent;
342     to->copyDate = copydate;
343     to->destroyMe = DESTROY_ME; /* Caller must always clear this!!! */
344     to->stamp.magic = VOLUMEINFOMAGIC;
345     to->stamp.version = VOLUMEINFOVERSION;
346     return 0;
347 }
348
349 afs_int32
350 CopyVolumeHeader(VolumeDiskData * from, VolumeDiskData * to)
351 {
352     afs_int32 code;
353
354     VOL_LOCK;
355     code = CopyVolumeHeader_r(from, to);
356     VOL_UNLOCK;
357     return (code);
358 }
359
360 void
361 ClearVolumeStats(register VolumeDiskData * vol)
362 {
363     VOL_LOCK;
364     ClearVolumeStats_r(vol);
365     VOL_UNLOCK;
366 }
367
368 void
369 ClearVolumeStats_r(register VolumeDiskData * vol)
370 {
371     memset(vol->weekUse, 0, sizeof(vol->weekUse));
372     vol->dayUse = 0;
373     vol->dayUseDate = 0;
374 }
375
376 /**
377  * read an existing volume disk header.
378  *
379  * @param[in]  volid  volume id
380  * @param[in]  dp     disk partition object
381  * @param[out] hdr    volume disk header
382  *
383  * @return operation status
384  *    @retval 0 success
385  *    @retval -1 volume header doesn't exist
386  *    @retval EIO failed to read volume header
387  *
388  * @internal
389  */
390 afs_int32
391 VReadVolumeDiskHeader(VolumeId volid,
392                       struct DiskPartition64 * dp,
393                       VolumeDiskHeader_t * hdr)
394 {
395     afs_int32 code = 0;
396     int fd;
397     char path[MAXPATHLEN];
398
399     (void)afs_snprintf(path, sizeof(path),
400                        "%s/" VFORMAT,
401                        VPartitionPath(dp), afs_printable_uint32_lu(volid));
402     fd = open(path, O_RDONLY);
403     if (fd < 0) {
404         Log("VReadVolumeDiskHeader: Couldn't open header for volume %lu.\n",
405             afs_printable_uint32_lu(volid));
406         code = -1;
407     } else if (read(fd, hdr, sizeof(*hdr)) != sizeof(*hdr)) {
408         Log("VReadVolumeDiskHeader: Couldn't read header for volume %lu.\n",
409             afs_printable_uint32_lu(volid));
410         code = EIO;
411     }
412
413     if (fd >= 0) {
414         close(fd);
415     }
416     return code;
417 }
418
419 /**
420  * write an existing volume disk header.
421  *
422  * @param[in] hdr   volume disk header
423  * @param[in] dp    disk partition object
424  * @param[in] cr    assert if O_CREAT | O_EXCL should be passed to open()
425  *
426  * @return operation status
427  *    @retval 0 success
428  *    @retval -1 volume header doesn't exist
429  *    @retval EIO failed to write volume header
430  *
431  * @internal
432  */
433 static afs_int32
434 _VWriteVolumeDiskHeader(VolumeDiskHeader_t * hdr,
435                         struct DiskPartition64 * dp,
436                         int flags)
437 {
438     afs_int32 code = 0;
439     int fd;
440     char path[MAXPATHLEN];
441
442     flags |= O_RDWR;
443
444     (void)afs_snprintf(path, sizeof(path),
445                        "%s/" VFORMAT,
446                        VPartitionPath(dp), afs_printable_uint32_lu(hdr->id));
447     fd = open(path, flags, 0644);
448     if (fd < 0) {
449         code = errno;
450         Log("_VWriteVolumeDiskHeader: Couldn't open header for volume %lu, "
451             "error = %d\n", afs_printable_uint32_lu(hdr->id), errno);
452     } else if (write(fd, hdr, sizeof(*hdr)) != sizeof(*hdr)) {
453         Log("_VWriteVolumeDiskHeader: Couldn't write header for volume %lu, "
454             "error = %d\n", afs_printable_uint32_lu(hdr->id), errno);
455         code = EIO;
456     }
457
458     if (fd >= 0) {
459         if (close(fd) != 0) {
460             Log("_VWriteVolumeDiskHeader: Error closing header for volume "
461                 "%lu, errno %d\n", afs_printable_uint32_lu(hdr->id), errno);
462         }
463     }
464
465     return code;
466 }
467
468 /**
469  * write an existing volume disk header.
470  *
471  * @param[in] hdr   volume disk header
472  * @param[in] dp    disk partition object
473  *
474  * @return operation status
475  *    @retval 0 success
476  *    @retval ENOENT volume header doesn't exist
477  *    @retval EIO failed to write volume header
478  */
479 afs_int32
480 VWriteVolumeDiskHeader(VolumeDiskHeader_t * hdr,
481                        struct DiskPartition64 * dp)
482 {
483     afs_int32 code;
484
485     code = _VWriteVolumeDiskHeader(hdr, dp, 0);
486     if (code) {
487         goto done;
488     }
489
490  done:
491     return code;
492 }
493
494 /**
495  * create and write a volume disk header to disk.
496  *
497  * @param[in] hdr   volume disk header
498  * @param[in] dp    disk partition object
499  *
500  * @return operation status
501  *    @retval 0 success
502  *    @retval EEXIST volume header already exists
503  *    @retval EIO failed to write volume header
504  *
505  * @internal
506  */
507 afs_int32
508 VCreateVolumeDiskHeader(VolumeDiskHeader_t * hdr,
509                         struct DiskPartition64 * dp)
510 {
511     afs_int32 code = 0;
512
513     code = _VWriteVolumeDiskHeader(hdr, dp, O_CREAT | O_EXCL);
514     if (code) {
515         goto done;
516     }
517
518  done:
519     return code;
520 }
521
522
523 /**
524  * destroy a volume disk header.
525  *
526  * @param[in] dp      disk partition object
527  * @param[in] volid   volume id
528  * @param[in] parent  parent's volume id, 0 if unknown
529  *
530  * @return operation status
531  *    @retval 0 success
532  *
533  * @note if parent is 0, the parent volume ID will be looked up from the
534  * fileserver
535  *
536  * @note for non-DAFS, parent is currently ignored
537  */
538 afs_int32
539 VDestroyVolumeDiskHeader(struct DiskPartition64 * dp,
540                          VolumeId volid,
541                          VolumeId parent)
542 {
543     afs_int32 code = 0;
544     char path[MAXPATHLEN];
545
546     (void)afs_snprintf(path, sizeof(path),
547                        "%s/" VFORMAT,
548                        VPartitionPath(dp), afs_printable_uint32_lu(volid));
549     code = unlink(path);
550     if (code) {
551         Log("VDestroyVolumeDiskHeader: Couldn't unlink disk header, error = %d\n", errno);
552         goto done;
553     }
554
555  done:
556     return code;
557 }
558