Further rationalise our usage of assert()
[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 #include <roken.h>
21
22 #ifdef HAVE_SYS_FILE_H
23 #include <sys/file.h>
24 #endif
25
26 #ifdef HAVE_SYS_LOCKF_H
27 #include <sys/lockf.h>
28 #endif
29
30 #include <afs/opr.h>
31 #include <rx/xdr.h>
32 #include <afs/afsint.h>
33 #include "nfs.h"
34 #include <afs/errors.h>
35 #include "lock.h"
36 #include "lwp.h"
37 #include <afs/afssyscalls.h>
38 #include "ihandle.h"
39 #include <afs/afsutil.h>
40 #ifdef AFS_NT40_ENV
41 #include "ntops.h"
42 #endif
43 #include "vnode.h"
44 #include "volume.h"
45 #include "volume_inline.h"
46 #include "partition.h"
47 #include "viceinode.h"
48
49 #include "volinodes.h"
50 #include "vol_prototypes.h"
51 #include "common.h"
52
53 #ifndef AFS_NT40_ENV
54 # ifdef O_LARGEFILE
55 #  define AFS_SETLKW   F_SETLKW64
56 #  define AFS_SETLK    F_SETLK64
57 #  define afs_st_flock flock64
58 # else
59 #  define AFS_SETLKW   F_SETLKW
60 #  define AFS_SETLK    F_SETLK
61 #  define afs_st_flock flock
62 # endif
63 #endif
64
65 /* Note:  the volume creation functions herein leave the destroyMe flag in the
66    volume header ON:  this means that the volumes will not be attached by the
67    file server and WILL BE DESTROYED the next time a system salvage is performed */
68
69 #ifdef FSSYNC_BUILD_CLIENT
70 static void
71 RemoveInodes(struct afs_inode_info *stuff, Device dev, VolumeId parent,
72              VolumeId vid)
73 {
74     int i;
75     IHandle_t *handle;
76
77     /* This relies on the fact that IDEC only needs the device and NT only
78      * needs the dev and vid to decrement volume special files.
79      */
80     IH_INIT(handle, dev, parent, -1);
81     for (i = 0; i < MAXINODETYPE; i++) {
82         Inode inode = *stuff[i].inode;
83         if (VALID_INO(inode)) {
84             if (stuff[i].inodeType == VI_LINKTABLE) {
85                 IH_DEC(handle, inode, parent);
86             } else {
87                 IH_DEC(handle, inode, vid);
88             }
89         }
90     }
91     IH_RELEASE(handle);
92 }
93
94 Volume *
95 VCreateVolume(Error * ec, char *partname, VolId volumeId, VolId parentId)
96 {                               /* Should be the same as volumeId if there is
97                                  * no parent */
98     Volume *retVal;
99     VOL_LOCK;
100     retVal = VCreateVolume_r(ec, partname, volumeId, parentId);
101     VOL_UNLOCK;
102     return retVal;
103 }
104
105 Volume *
106 VCreateVolume_r(Error * ec, char *partname, VolId volumeId, VolId parentId)
107 {                               /* Should be the same as volumeId if there is
108                                  * no parent */
109     VolumeDiskData vol;
110     int i, rc;
111     char headerName[VMAXPATHLEN], volumePath[VMAXPATHLEN];
112     Device device;
113     struct DiskPartition64 *partition;
114     struct VolumeDiskHeader diskHeader;
115     IHandle_t *handle;
116     FdHandle_t *fdP;
117     Inode nearInode AFS_UNUSED = 0;
118     char *part, *name;
119     struct stat st;
120     struct VolumeHeader tempHeader;
121     struct afs_inode_info stuff[MAXINODETYPE];
122     afs_ino_str_t stmp;
123 # ifdef AFS_DEMAND_ATTACH_FS
124     int locktype = 0;
125 # endif /* AFS_DEMAND_ATTACH_FS */
126
127     init_inode_info(&tempHeader, stuff);
128
129     *ec = 0;
130     memset(&vol, 0, sizeof(vol));
131     vol.id = volumeId;
132     vol.parentId = parentId;
133     vol.copyDate = time(0);     /* The only date which really means when this
134                                  * @i(instance) of this volume was created.
135                                  * Creation date does not mean this */
136
137     /* Initialize handle for error case below. */
138     handle = NULL;
139
140     /* Verify that the parition is valid before writing to it. */
141     if (!(partition = VGetPartition_r(partname, 0))) {
142         Log("VCreateVolume: partition %s is not in service.\n", partname);
143         *ec = VNOVOL;
144         return NULL;
145     }
146 #if     defined(NEARINODE_HINT)
147     nearInodeHash(volumeId, nearInode);
148     nearInode %= partition->f_files;
149 #endif
150     VGetVolumePath(ec, vol.id, &part, &name);
151     if (*ec == VNOVOL || !strcmp(partition->name, part)) {
152         /* this case is ok */
153     } else {
154         /* return EXDEV if it's a clone to an alternate partition
155          * otherwise assume it's a move */
156         if (vol.parentId != vol.id) {
157             *ec = EXDEV;
158             return NULL;
159         }
160     }
161     *ec = 0;
162
163 # ifdef AFS_DEMAND_ATTACH_FS
164     /* volume doesn't exist yet, but we must lock it to try to prevent something
165      * else from reading it when we're e.g. half way through creating it (or
166      * something tries to create the same volume at the same time) */
167     locktype = VVolLockType(V_VOLUPD, 1);
168     rc = VLockVolumeByIdNB(volumeId, partition, locktype);
169     if (rc) {
170         Log("VCreateVolume: vol %lu already locked by someone else\n",
171             afs_printable_uint32_lu(volumeId));
172         *ec = VNOVOL;
173         return NULL;
174     }
175 # else /* AFS_DEMAND_ATTACH_FS */
176     VLockPartition_r(partname);
177 # endif /* !AFS_DEMAND_ATTACH_FS */
178
179     memset(&tempHeader, 0, sizeof(tempHeader));
180     tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
181     tempHeader.stamp.version = VOLUMEHEADERVERSION;
182     tempHeader.id = vol.id;
183     tempHeader.parent = vol.parentId;
184     vol.stamp.magic = VOLUMEINFOMAGIC;
185     vol.stamp.version = VOLUMEINFOVERSION;
186     vol.destroyMe = DESTROY_ME;
187     snprintf(headerName, sizeof headerName, VFORMAT,
188              afs_printable_uint32_lu(vol.id));
189     snprintf(volumePath, sizeof volumePath, "%s" OS_DIRSEP "%s",
190              VPartitionPath(partition), headerName);
191     rc = stat(volumePath, &st);
192     if (rc == 0 || errno != ENOENT) {
193         if (rc == 0) {
194             Log("VCreateVolume: Header file %s already exists!\n",
195                 volumePath);
196             *ec = VVOLEXISTS;
197         } else {
198             Log("VCreateVolume: Error %d trying to stat header file %s\n",
199                 errno, volumePath);
200             *ec = VNOVOL;
201         }
202         goto bad_noheader;
203     }
204     device = partition->device;
205
206     for (i = 0; i < MAXINODETYPE; i++) {
207         struct afs_inode_info *p = &stuff[i];
208         if (p->obsolete)
209             continue;
210 #ifdef AFS_NAMEI_ENV
211         *(p->inode) =
212             IH_CREATE(NULL, device, VPartitionPath(partition), nearInode,
213                       (p->inodeType == VI_LINKTABLE) ? vol.parentId : vol.id,
214                       INODESPECIAL, p->inodeType, vol.parentId);
215         if (!(VALID_INO(*(p->inode)))) {
216             if (errno == EEXIST && (p->inodeType == VI_LINKTABLE)) {
217                 /* Increment the reference count instead. */
218                 IHandle_t *lh;
219                 int code;
220
221                 *(p->inode) = namei_MakeSpecIno(vol.parentId, VI_LINKTABLE);
222                 IH_INIT(lh, device, parentId, *(p->inode));
223                 fdP = IH_OPEN(lh);
224                 if (fdP == NULL) {
225                     IH_RELEASE(lh);
226                     goto bad;
227                 }
228                 code = IH_INC(lh, *(p->inode), parentId);
229                 FDH_REALLYCLOSE(fdP);
230                 IH_RELEASE(lh);
231                 if (code < 0)
232                     goto bad;
233                 continue;
234             }
235         }
236 #else
237         *(p->inode) =
238             IH_CREATE(NULL, device, VPartitionPath(partition), nearInode,
239                       vol.id, INODESPECIAL, p->inodeType, vol.parentId);
240 #endif
241
242         if (!VALID_INO(*(p->inode))) {
243             Log("VCreateVolume:  Problem creating %s file associated with volume header %s\n", p->description, volumePath);
244           bad:
245             if (handle)
246                 IH_RELEASE(handle);
247             RemoveInodes(stuff, device, vol.parentId, vol.id);
248             if (!*ec) {
249                 *ec = VNOVOL;
250             }
251             VDestroyVolumeDiskHeader(partition, volumeId, parentId);
252           bad_noheader:
253 # ifdef AFS_DEMAND_ATTACH_FS
254             if (locktype) {
255                 VUnlockVolumeById(volumeId, partition);
256             }
257 # endif /* AFS_DEMAND_ATTACH_FS */
258             return NULL;
259         }
260         IH_INIT(handle, device, vol.parentId, *(p->inode));
261         fdP = IH_OPEN(handle);
262         if (fdP == NULL) {
263             Log("VCreateVolume:  Problem iopen inode %s (err=%d)\n",
264                 PrintInode(stmp, *(p->inode)), errno);
265             goto bad;
266         }
267         if (FDH_PWRITE(fdP, (char *)&p->stamp, sizeof(p->stamp), 0) !=
268             sizeof(p->stamp)) {
269             Log("VCreateVolume:  Problem writing to inode %s (err=%d)\n",
270                 PrintInode(stmp, *(p->inode)), errno);
271             FDH_REALLYCLOSE(fdP);
272             goto bad;
273         }
274         FDH_REALLYCLOSE(fdP);
275         IH_RELEASE(handle);
276         nearInode = *(p->inode);
277     }
278
279     IH_INIT(handle, device, vol.parentId, tempHeader.volumeInfo);
280     fdP = IH_OPEN(handle);
281     if (fdP == NULL) {
282         Log("VCreateVolume:  Problem iopen inode %s (err=%d)\n",
283             PrintInode(stmp, tempHeader.volumeInfo), errno);
284         goto bad;
285     }
286     if (FDH_PWRITE(fdP, (char *)&vol, sizeof(vol), 0) != sizeof(vol)) {
287         Log("VCreateVolume:  Problem writing to  inode %s (err=%d)\n",
288             PrintInode(stmp, tempHeader.volumeInfo), errno);
289         FDH_REALLYCLOSE(fdP);
290         goto bad;
291     }
292     FDH_CLOSE(fdP);
293     IH_RELEASE(handle);
294
295     VolumeHeaderToDisk(&diskHeader, &tempHeader);
296     rc = VCreateVolumeDiskHeader(&diskHeader, partition);
297     if (rc) {
298         Log("VCreateVolume: Error %d trying to write volume header for "
299             "volume %u on partition %s; volume not created\n", rc,
300             vol.id, VPartitionPath(partition));
301         if (rc == EEXIST) {
302             *ec = VVOLEXISTS;
303         }
304         goto bad;
305     }
306
307 # ifdef AFS_DEMAND_ATTACH_FS
308     if (locktype) {
309         VUnlockVolumeById(volumeId, partition);
310     }
311 # endif /* AFS_DEMAND_ATTACH_FS */
312     return (VAttachVolumeByName_r(ec, partname, headerName, V_SECRETLY));
313 }
314 #endif /* FSSYNC_BUILD_CLIENT */
315
316
317 void
318 AssignVolumeName(VolumeDiskData * vol, char *name, char *ext)
319 {
320     VOL_LOCK;
321     AssignVolumeName_r(vol, name, ext);
322     VOL_UNLOCK;
323 }
324
325 void
326 AssignVolumeName_r(VolumeDiskData * vol, char *name, char *ext)
327 {
328     char *dot;
329     strncpy(vol->name, name, VNAMESIZE - 1);
330     vol->name[VNAMESIZE - 1] = '\0';
331     dot = strrchr(vol->name, '.');
332     if (dot && (strcmp(dot, ".backup") == 0 || strcmp(dot, ".readonly") == 0))
333         *dot = 0;
334     if (ext)
335         strncat(vol->name, ext, VNAMESIZE - 1 - strlen(vol->name));
336 }
337
338 afs_int32
339 CopyVolumeHeader_r(VolumeDiskData * from, VolumeDiskData * to)
340 {
341     /* The id and parentId fields are not copied; these are inviolate--the to volume
342      * is assumed to have already been created.  The id's cannot be changed once
343      * creation has taken place, since they are embedded in the various inodes associated
344      * with the volume.  The copydate is also inviolate--it always reflects the time
345      * this volume was created (compare with the creation date--the creation date of
346      * a backup volume is the creation date of the original parent, because the backup
347      * is used to backup the parent volume). */
348     Date copydate;
349     VolumeId id, parent;
350     id = to->id;
351     parent = to->parentId;
352     copydate = to->copyDate;
353     memcpy(to, from, sizeof(*from));
354     to->id = id;
355     to->parentId = parent;
356     to->copyDate = copydate;
357     to->destroyMe = DESTROY_ME; /* Caller must always clear this!!! */
358     to->stamp.magic = VOLUMEINFOMAGIC;
359     to->stamp.version = VOLUMEINFOVERSION;
360     return 0;
361 }
362
363 afs_int32
364 CopyVolumeHeader(VolumeDiskData * from, VolumeDiskData * to)
365 {
366     afs_int32 code;
367
368     VOL_LOCK;
369     code = CopyVolumeHeader_r(from, to);
370     VOL_UNLOCK;
371     return (code);
372 }
373
374 void
375 ClearVolumeStats(VolumeDiskData * vol)
376 {
377     VOL_LOCK;
378     ClearVolumeStats_r(vol);
379     VOL_UNLOCK;
380 }
381
382 void
383 ClearVolumeStats_r(VolumeDiskData * vol)
384 {
385     memset(vol->weekUse, 0, sizeof(vol->weekUse));
386     vol->dayUse = 0;
387     vol->dayUseDate = 0;
388 }
389
390 void
391 CopyVolumeStats_r(VolumeDiskData * from, VolumeDiskData * to)
392 {
393     memcpy(to->weekUse, from->weekUse, sizeof(to->weekUse));
394     to->dayUse = from->dayUse;
395     to->dayUseDate = from->dayUseDate;
396     if (from->stat_initialized) {
397         memcpy(to->stat_reads, from->stat_reads, sizeof(to->stat_reads));
398         memcpy(to->stat_writes, from->stat_writes, sizeof(to->stat_writes));
399         memcpy(to->stat_fileSameAuthor, from->stat_fileSameAuthor,
400                sizeof(to->stat_fileSameAuthor));
401         memcpy(to->stat_fileDiffAuthor, from->stat_fileDiffAuthor,
402                sizeof(to->stat_fileDiffAuthor));
403         memcpy(to->stat_dirSameAuthor, from->stat_dirSameAuthor,
404                sizeof(to->stat_dirSameAuthor));
405         memcpy(to->stat_dirDiffAuthor, from->stat_dirDiffAuthor,
406                sizeof(to->stat_dirDiffAuthor));
407     }
408 }
409
410 void
411 CopyVolumeStats(VolumeDiskData * from, VolumeDiskData * to)
412 {
413     VOL_LOCK;
414     CopyVolumeStats_r(from, to);
415     VOL_UNLOCK;
416 }
417
418 /**
419  * read an existing volume disk header.
420  *
421  * @param[in]  volid  volume id
422  * @param[in]  dp     disk partition object
423  * @param[out] hdr    volume disk header or NULL
424  *
425  * @note if hdr is NULL, this is essentially an existence test for the vol
426  *       header
427  *
428  * @return operation status
429  *    @retval 0 success
430  *    @retval -1 volume header doesn't exist
431  *    @retval EIO failed to read volume header
432  *
433  * @internal
434  */
435 afs_int32
436 VReadVolumeDiskHeader(VolumeId volid,
437                       struct DiskPartition64 * dp,
438                       VolumeDiskHeader_t * hdr)
439 {
440     afs_int32 code = 0;
441     int fd;
442     char path[MAXPATHLEN];
443
444     snprintf(path, sizeof(path), "%s" OS_DIRSEP VFORMAT,
445              VPartitionPath(dp), afs_printable_uint32_lu(volid));
446     fd = open(path, O_RDONLY);
447     if (fd < 0) {
448         Log("VReadVolumeDiskHeader: Couldn't open header for volume %lu (errno %d).\n",
449             afs_printable_uint32_lu(volid), errno);
450         code = -1;
451
452     } else if (hdr && read(fd, hdr, sizeof(*hdr)) != sizeof(*hdr)) {
453         Log("VReadVolumeDiskHeader: Couldn't read header for volume %lu.\n",
454             afs_printable_uint32_lu(volid));
455         code = EIO;
456     }
457
458     if (fd >= 0) {
459         close(fd);
460     }
461     return code;
462 }
463
464 #ifdef FSSYNC_BUILD_CLIENT
465 /**
466  * write an existing volume disk header.
467  *
468  * @param[in] hdr   volume disk header
469  * @param[in] dp    disk partition object
470  * @param[in] cr    assert if O_CREAT | O_EXCL should be passed to open()
471  *
472  * @return operation status
473  *    @retval 0 success
474  *    @retval -1 volume header doesn't exist
475  *    @retval EIO failed to write volume header
476  *
477  * @internal
478  */
479 static afs_int32
480 _VWriteVolumeDiskHeader(VolumeDiskHeader_t * hdr,
481                         struct DiskPartition64 * dp,
482                         int flags)
483 {
484     afs_int32 code = 0;
485     int fd;
486     char path[MAXPATHLEN];
487
488 #ifdef AFS_DEMAND_ATTACH_FS
489     /* prevent racing with VGC scanners reading the vol header while we are
490      * writing it */
491     code = VPartHeaderLock(dp, READ_LOCK);
492     if (code) {
493         return EIO;
494     }
495 #endif /* AFS_DEMAND_ATTACH_FS */
496
497     flags |= O_RDWR;
498
499     snprintf(path, sizeof(path), "%s" OS_DIRSEP VFORMAT,
500              VPartitionPath(dp), afs_printable_uint32_lu(hdr->id));
501     fd = open(path, flags, 0644);
502     if (fd < 0) {
503         code = errno;
504         Log("_VWriteVolumeDiskHeader: Couldn't open header for volume %lu, "
505             "error = %d\n", afs_printable_uint32_lu(hdr->id), errno);
506     } else if (write(fd, hdr, sizeof(*hdr)) != sizeof(*hdr)) {
507         Log("_VWriteVolumeDiskHeader: Couldn't write header for volume %lu, "
508             "error = %d\n", afs_printable_uint32_lu(hdr->id), errno);
509         code = EIO;
510     }
511
512     if (fd >= 0) {
513         if (close(fd) != 0) {
514             Log("_VWriteVolumeDiskHeader: Error closing header for volume "
515                 "%lu, errno %d\n", afs_printable_uint32_lu(hdr->id), errno);
516         }
517     }
518
519 #ifdef AFS_DEMAND_ATTACH_FS
520     VPartHeaderUnlock(dp, READ_LOCK);
521 #endif /* AFS_DEMAND_ATTACH_FS */
522
523     return code;
524 }
525
526 /**
527  * write an existing volume disk header.
528  *
529  * @param[in] hdr   volume disk header
530  * @param[in] dp    disk partition object
531  *
532  * @return operation status
533  *    @retval 0 success
534  *    @retval ENOENT volume header doesn't exist
535  *    @retval EIO failed to write volume header
536  */
537 afs_int32
538 VWriteVolumeDiskHeader(VolumeDiskHeader_t * hdr,
539                        struct DiskPartition64 * dp)
540 {
541     afs_int32 code;
542
543 #ifdef AFS_DEMAND_ATTACH_FS
544     VolumeDiskHeader_t oldhdr;
545     int delvgc = 0, addvgc = 0;
546     SYNC_response res;
547
548     /* first, see if anything with the volume IDs have changed; if so, we
549      * need to update the VGC */
550
551     code = VReadVolumeDiskHeader(hdr->id, dp, &oldhdr);
552     if (code == 0 && (oldhdr.id != hdr->id || oldhdr.parent != hdr->parent)) {
553         /* the vol id or parent vol id changed; need to delete the VGC entry
554          * for the old vol id/parent, and add the new one */
555         delvgc = 1;
556         addvgc = 1;
557
558     } else if (code) {
559         /* couldn't get the old header info; add the new header info to the
560          * VGC in case it hasn't been added yet */
561         addvgc = 1;
562     }
563
564 #endif /* AFS_DEMAND_ATTACH_FS */
565
566     code = _VWriteVolumeDiskHeader(hdr, dp, 0);
567     if (code) {
568         goto done;
569     }
570
571 #ifdef AFS_DEMAND_ATTACH_FS
572     if (delvgc) {
573         memset(&res, 0, sizeof(res));
574         code = FSYNC_VGCDel(dp->name, oldhdr.parent, oldhdr.id, FSYNC_WHATEVER, &res);
575
576         /* unknown vol id is okay; it just further suggests the old header
577          * data was bogus, which is fine since we're trying to fix it */
578         if (code && res.hdr.reason != FSYNC_UNKNOWN_VOLID) {
579             Log("VWriteVolumeDiskHeader: FSYNC_VGCDel(%s, %lu, %lu) "
580                 "failed with code %ld reason %ld\n", dp->name,
581                 afs_printable_uint32_lu(oldhdr.parent),
582                 afs_printable_uint32_lu(oldhdr.id),
583                 afs_printable_int32_ld(code),
584                 afs_printable_int32_ld(res.hdr.reason));
585         }
586
587     }
588     if (addvgc) {
589         memset(&res, 0, sizeof(res));
590         code = FSYNC_VGCAdd(dp->name, hdr->parent, hdr->id, FSYNC_WHATEVER, &res);
591         if (code) {
592             Log("VWriteVolumeDiskHeader: FSYNC_VGCAdd(%s, %lu, %lu) "
593                 "failed with code %ld reason %ld\n", dp->name,
594                 afs_printable_uint32_lu(hdr->parent),
595                 afs_printable_uint32_lu(hdr->id),
596                 afs_printable_int32_ld(code),
597                 afs_printable_int32_ld(res.hdr.reason));
598         }
599     }
600
601 #endif /* AFS_DEMAND_ATTACH_FS */
602
603  done:
604     return code;
605 }
606
607 /**
608  * create and write a volume disk header to disk.
609  *
610  * @param[in] hdr   volume disk header
611  * @param[in] dp    disk partition object
612  *
613  * @return operation status
614  *    @retval 0 success
615  *    @retval EEXIST volume header already exists
616  *    @retval EIO failed to write volume header
617  *
618  * @internal
619  */
620 afs_int32
621 VCreateVolumeDiskHeader(VolumeDiskHeader_t * hdr,
622                         struct DiskPartition64 * dp)
623 {
624     afs_int32 code = 0;
625 #ifdef AFS_DEMAND_ATTACH_FS
626     SYNC_response res;
627 #endif /* AFS_DEMAND_ATTACH_FS */
628
629     code = _VWriteVolumeDiskHeader(hdr, dp, O_CREAT | O_EXCL);
630     if (code) {
631         goto done;
632     }
633
634 #ifdef AFS_DEMAND_ATTACH_FS
635     memset(&res, 0, sizeof(res));
636     code = FSYNC_VGCAdd(dp->name, hdr->parent, hdr->id, FSYNC_WHATEVER, &res);
637     if (code) {
638         Log("VCreateVolumeDiskHeader: FSYNC_VGCAdd(%s, %lu, %lu) failed "
639             "with code %ld reason %ld\n", dp->name,
640             afs_printable_uint32_lu(hdr->parent),
641             afs_printable_uint32_lu(hdr->id),
642             afs_printable_int32_ld(code),
643             afs_printable_int32_ld(res.hdr.reason));
644     }
645 #endif /* AFS_DEMAND_ATTACH_FS */
646
647  done:
648     return code;
649 }
650
651
652 /**
653  * destroy a volume disk header.
654  *
655  * @param[in] dp      disk partition object
656  * @param[in] volid   volume id
657  * @param[in] parent  parent's volume id, 0 if unknown
658  *
659  * @return operation status
660  *    @retval 0 success
661  *
662  * @note if parent is 0, the parent volume ID will be looked up from the
663  * fileserver
664  *
665  * @note for non-DAFS, parent is currently ignored
666  */
667 afs_int32
668 VDestroyVolumeDiskHeader(struct DiskPartition64 * dp,
669                          VolumeId volid,
670                          VolumeId parent)
671 {
672     afs_int32 code = 0;
673     char path[MAXPATHLEN];
674 #ifdef AFS_DEMAND_ATTACH_FS
675     SYNC_response res;
676 #endif /* AFS_DEMAND_ATTACH_FS */
677
678     snprintf(path, sizeof(path), "%s" OS_DIRSEP VFORMAT,
679              VPartitionPath(dp), afs_printable_uint32_lu(volid));
680     code = unlink(path);
681     if (code) {
682         Log("VDestroyVolumeDiskHeader: Couldn't unlink disk header, error = %d\n", errno);
683         goto done;
684     }
685
686 #ifdef AFS_DEMAND_ATTACH_FS
687     memset(&res, 0, sizeof(res));
688     if (!parent) {
689         FSSYNC_VGQry_response_t q_res;
690
691         code = FSYNC_VGCQuery(dp->name, volid, &q_res, &res);
692         if (code) {
693             Log("VDestroyVolumeDiskHeader: FSYNC_VGCQuery(%s, %lu) failed "
694                 "with code %ld, reason %ld\n", dp->name,
695                 afs_printable_uint32_lu(volid), afs_printable_int32_ld(code),
696                 afs_printable_int32_ld(res.hdr.reason));
697             goto done;
698         }
699
700         parent = q_res.rw;
701
702     }
703     code = FSYNC_VGCDel(dp->name, parent, volid, FSYNC_WHATEVER, &res);
704     if (code) {
705         Log("VDestroyVolumeDiskHeader: FSYNC_VGCDel(%s, %lu, %lu) failed "
706             "with code %ld reason %ld\n", dp->name,
707             afs_printable_uint32_lu(parent),
708             afs_printable_uint32_lu(volid),
709             afs_printable_int32_ld(code),
710             afs_printable_int32_ld(res.hdr.reason));
711     }
712 #endif /* AFS_DEMAND_ATTACH_FS */
713
714  done:
715     return code;
716 }
717 #endif /* FSSYNC_BUILD_CLIENT */
718
719 /**
720  * handle a single vol header as part of VWalkVolumeHeaders.
721  *
722  * @param[in] dp      disk partition
723  * @param[in] volfunc function to call when a vol header is successfully read
724  * @param[in] name    full path name to the .vol header
725  * @param[out] hdr    header data read in from the .vol header
726  * @param[in] locked  1 if the partition headers are locked, 0 otherwise
727  * @param[in] rock    the rock to pass to volfunc
728  *
729  * @return operation status
730  *  @retval 0  success
731  *  @retval -1 fatal error, stop scanning
732  *  @retval 1  failed to read header
733  *  @retval 2  volfunc callback indicated error after header read
734  */
735 static int
736 _VHandleVolumeHeader(struct DiskPartition64 *dp, VWalkVolFunc volfunc,
737                      const char *name, struct VolumeDiskHeader *hdr,
738                      int locked, void *rock)
739 {
740     int error = 0;
741     FD_t fd;
742
743     if ((fd = OS_OPEN(name, O_RDONLY, 0)) == INVALID_FD
744         || OS_READ(fd, hdr, sizeof(*hdr))
745         != sizeof(*hdr)
746         || hdr->stamp.magic != VOLUMEHEADERMAGIC) {
747         error = 1;
748     }
749
750     if (fd != INVALID_FD) {
751         OS_CLOSE(fd);
752     }
753
754 #ifdef AFSFS_DEMAND_ATTACH_FS
755     if (locked) {
756         VPartHeaderUnlock(dp);
757     }
758 #endif /* AFS_DEMAND_ATTACH_FS */
759
760     if (!error && volfunc) {
761         /* the volume header seems fine; call the caller-supplied
762          * 'we-found-a-volume-header' function */
763         int last = 1;
764
765 #ifdef AFS_DEMAND_ATTACH_FS
766         if (!locked) {
767             last = 0;
768         }
769 #endif /* AFS_DEMAND_ATTACH_FS */
770
771         error = (*volfunc) (dp, name, hdr, last, rock);
772         if (error < 0) {
773             return -1;
774         }
775         if (error) {
776             error = 2;
777         }
778     }
779
780 #ifdef AFS_DEMAND_ATTACH_FS
781     if (error && !locked) {
782         int code;
783         /* retry reading the volume header under the partition
784          * header lock, just to be safe and ensure we're not
785          * racing something rewriting the vol header */
786         code = VPartHeaderLock(dp, WRITE_LOCK);
787         if (code) {
788             Log("Error acquiring partition write lock when "
789                 "looking at header %s\n", name);
790             return -1;
791         }
792
793         return _VHandleVolumeHeader(dp, volfunc, name, hdr, 1, rock);
794     }
795 #endif /* AFS_DEMAND_ATTACH_FS */
796
797     return error;
798 }
799
800 /**
801  * walk through the list of volume headers on a partition.
802  *
803  * This function looks through all of the .vol headers on a partition, reads in
804  * each header, and calls the supplied volfunc function on each one. If the
805  * header cannot be read (or volfunc returns a positive error code), DAFS will
806  * VPartHeaderExLock() and retry. If that fails, or if we are non-DAFS, errfunc
807  * will be called (which typically will unlink the problem volume header).
808  *
809  * If volfunc returns a negative error code, walking the partition will stop
810  * and we will return an error immediately.
811  *
812  * @param[in] dp       partition to walk
813  * @param[in] partpath the path opendir()
814  * @param[in] volfunc  the function to call when a header is encountered, or
815  *                     NULL to just skip over valid headers
816  * @param[in] errfunc  the function to call when a problematic header is
817  *                     encountered, or NULL to just skip over bad headers
818  * @param[in] rock     rock for volfunc and errfunc
819  *
820  * @see VWalkVolFunc
821  * @see VWalkErrFunc
822  *
823  * @return operation status
824  *  @retval 0 success
825  *  @retval negative fatal error, walk did not finish
826  */
827 int
828 VWalkVolumeHeaders(struct DiskPartition64 *dp, const char *partpath,
829                    VWalkVolFunc volfunc, VWalkErrFunc errfunc, void *rock)
830 {
831     DIR *dirp;
832     struct dirent *dentry;
833     int code = 0;
834     struct VolumeDiskHeader diskHeader;
835
836     dirp = opendir(partpath);
837     if (!dirp) {
838         Log("VWalkVolumeHeaders: cannot open directory %s\n", partpath);
839         code = -1;
840         goto done;
841     }
842
843     while ((dentry = readdir(dirp)) != NULL) {
844         char *p;
845         p = strrchr(dentry->d_name, '.');
846         if (p != NULL && strcmp(p, VHDREXT) == 0) {
847             char name[VMAXPATHLEN];
848
849             sprintf(name, "%s" OS_DIRSEP "%s", partpath, dentry->d_name);
850
851             code = _VHandleVolumeHeader(dp, volfunc, name, &diskHeader, -1, rock);
852             if (code < 0) {
853                 /* fatal error, stop walking */
854                 goto done;
855             }
856             if (code && errfunc) {
857                 /* error with header; call the caller-supplied vol error
858                  * function */
859
860                 struct VolumeDiskHeader *hdr = &diskHeader;
861                 if (code == 1) {
862                     /* we failed to read the header at all, so don't pass in
863                      * the header ptr */
864                     hdr = NULL;
865                 }
866                 (*errfunc) (dp, name, hdr, rock);
867             }
868             code = 0;
869         }
870     }
871  done:
872     if (dirp) {
873         closedir(dirp);
874         dirp = NULL;
875     }
876
877     return code;
878 }
879
880 /**
881  * initialize a struct VLockFile.
882  *
883  * @param[in] lf   struct VLockFile to initialize
884  * @param[in] path Full path to the file to use for locks. The string contents
885  *                 are copied.
886  */
887 void
888 VLockFileInit(struct VLockFile *lf, const char *path)
889 {
890     memset(lf, 0, sizeof(*lf));
891     lf->path = strdup(path);
892     lf->fd = INVALID_FD;
893     MUTEX_INIT(&lf->mutex, "vlockfile", MUTEX_DEFAULT, 0);
894 }
895
896 #ifdef AFS_NT40_ENV
897 static_inline FD_t
898 _VOpenPath(const char *path)
899 {
900     HANDLE handle;
901
902     handle = CreateFile(path,
903                         GENERIC_READ | GENERIC_WRITE,
904                         FILE_SHARE_READ | FILE_SHARE_WRITE,
905                         NULL,
906                         OPEN_ALWAYS,
907                         FILE_ATTRIBUTE_HIDDEN,
908                         NULL);
909     if (handle == INVALID_HANDLE_VALUE) {
910         return INVALID_FD;
911     }
912
913     return handle;
914 }
915
916 static_inline int
917 _VLockFd(FD_t handle, afs_uint32 offset, int locktype, int nonblock)
918 {
919     DWORD flags = 0;
920     OVERLAPPED lap;
921
922     if (locktype == WRITE_LOCK) {
923         flags |= LOCKFILE_EXCLUSIVE_LOCK;
924     }
925     if (nonblock) {
926         flags |= LOCKFILE_FAIL_IMMEDIATELY;
927     }
928
929     memset(&lap, 0, sizeof(lap));
930     lap.Offset = offset;
931
932     if (!LockFileEx(handle, flags, 0, 1, 0, &lap)) {
933         if (GetLastError() == ERROR_LOCK_VIOLATION) {
934             return EBUSY;
935         }
936         return EIO;
937     }
938
939     return 0;
940 }
941
942 static_inline void
943 _VUnlockFd(FD_t handle, afs_uint32 offset)
944 {
945     OVERLAPPED lap;
946
947     memset(&lap, 0, sizeof(lap));
948     lap.Offset = offset;
949
950     UnlockFileEx(handle, 0, 1, 0, &lap);
951 }
952
953 static_inline void
954 _VCloseFd(FD_t handle)
955 {
956     CloseHandle(handle);
957 }
958
959 #else /* !AFS_NT40_ENV */
960
961 /**
962  * open a file on the local filesystem suitable for locking
963  *
964  * @param[in] path  abs path of the file to open
965  *
966  * @return file descriptor
967  *  @retval INVALID_FD failure opening file
968  */
969 static_inline FD_t
970 _VOpenPath(const char *path)
971 {
972     int fd;
973
974     fd = open(path, O_RDWR | O_CREAT, 0660);
975     if (fd < 0) {
976         return INVALID_FD;
977     }
978     return fd;
979 }
980
981 /**
982  * lock an offset in a file descriptor.
983  *
984  * @param[in] fd       file descriptor to lock
985  * @param[in] offset   offset in file to lock
986  * @param[in] locktype READ_LOCK or WRITE_LOCK
987  * @param[in] nonblock 1 to fail immediately, 0 to wait to acquire lock
988  *
989  * @return operation status
990  *  @retval 0 success
991  *  @retval EBUSY someone else is holding a conflicting lock and nonblock=1 was
992  *                specified
993  *  @retval EIO   error acquiring file lock
994  */
995 static_inline int
996 _VLockFd(FD_t fd, afs_uint32 offset, int locktype, int nonblock)
997 {
998     int l_type = F_WRLCK;
999     int cmd = AFS_SETLKW;
1000     struct afs_st_flock sf;
1001
1002     if (locktype == READ_LOCK) {
1003         l_type = F_RDLCK;
1004     }
1005     if (nonblock) {
1006         cmd = AFS_SETLK;
1007     }
1008
1009     sf.l_start = offset;
1010     sf.l_len = 1;
1011     sf.l_type = l_type;
1012     sf.l_whence = SEEK_SET;
1013
1014     if (fcntl(fd, cmd, &sf)) {
1015         if (nonblock && (errno == EACCES || errno == EAGAIN)) {
1016             /* We asked for a nonblocking lock, and it was already locked */
1017             sf.l_pid = 0;
1018             if (fcntl(fd, F_GETLK, &sf) != 0 || sf.l_pid == 0) {
1019                 Log("_VLockFd: fcntl failed with error %d when trying to "
1020                     "query the conflicting lock for fd %d (locktype=%d, "
1021                     "offset=%lu)\n", errno, fd, locktype,
1022                     afs_printable_uint32_lu(offset));
1023             } else {
1024                 Log("_VLockFd: conflicting lock held on fd %d, offset %lu by "
1025                     "pid %ld (locktype=%d)\n", fd,
1026                     afs_printable_uint32_lu(offset), (long int)sf.l_pid,
1027                     locktype);
1028             }
1029             return EBUSY;
1030         }
1031         Log("_VLockFd: fcntl failed with error %d when trying to lock "
1032             "fd %d (locktype=%d, offset=%lu)\n", errno, fd, locktype,
1033             afs_printable_uint32_lu(offset));
1034         return EIO;
1035     }
1036
1037     return 0;
1038 }
1039
1040 /**
1041  * close a file descriptor used for file locking.
1042  *
1043  * @param[in] fd file descriptor to close
1044  */
1045 static_inline void
1046 _VCloseFd(FD_t fd)
1047 {
1048     if (close(fd)) {
1049         Log("_VCloseFd: error %d closing fd %d\n",
1050             errno, fd);
1051     }
1052 }
1053
1054 /**
1055  * unlock a file offset in a file descriptor.
1056  *
1057  * @param[in] fd file descriptor to unlock
1058  * @param[in] offset offset to unlock
1059  */
1060 static_inline void
1061 _VUnlockFd(FD_t fd, afs_uint32 offset)
1062 {
1063     struct afs_st_flock sf;
1064
1065     sf.l_start = offset;
1066     sf.l_len = 1;
1067     sf.l_type = F_UNLCK;
1068     sf.l_whence = SEEK_SET;
1069
1070     if (fcntl(fd, AFS_SETLK, &sf)) {
1071         Log("_VUnlockFd: fcntl failed with error %d when trying to unlock "
1072             "fd %d\n", errno, fd);
1073     }
1074 }
1075 #endif /* !AFS_NT40_ENV */
1076
1077 /**
1078  * reinitialize a struct VLockFile.
1079  *
1080  * Use this to close the lock file (unlocking any locks in it), and effectively
1081  * restore lf to the state it was in when it was initialized. This is the same
1082  * as unlocking all of the locks on the file, without having to remember what
1083  * all of the locks were. Do not unlock previously held locks after calling
1084  * this.
1085  *
1086  * @param[in] lf  struct VLockFile to reinit
1087  *
1088  * @pre nobody is waiting for a lock on this lockfile or otherwise using
1089  *      this lockfile at all
1090  */
1091 void
1092 VLockFileReinit(struct VLockFile *lf)
1093 {
1094     MUTEX_ENTER(&lf->mutex);
1095
1096     if (lf->fd != INVALID_FD) {
1097         _VCloseFd(lf->fd);
1098         lf->fd = INVALID_FD;
1099     }
1100
1101     lf->refcount = 0;
1102
1103     MUTEX_EXIT(&lf->mutex);
1104 }
1105
1106 /**
1107  * lock a file on disk for the process.
1108  *
1109  * @param[in] lf       the struct VLockFile representing the file to lock
1110  * @param[in] offset   the offset in the file to lock
1111  * @param[in] locktype READ_LOCK or WRITE_LOCK
1112  * @param[in] nonblock 0 to wait for conflicting locks to clear before
1113  *                     obtaining the lock; 1 to fail immediately if a
1114  *                     conflicting lock is held by someone else
1115  *
1116  * @return operation status
1117  *  @retval 0 success
1118  *  @retval EBUSY someone else is holding a conflicting lock and nonblock=1 was
1119  *                specified
1120  *  @retval EIO   error acquiring file lock
1121  *
1122  * @note DAFS only
1123  *
1124  * @note do not try to lock/unlock the same offset in the same file from
1125  * different threads; use VGetDiskLock to protect threads from each other in
1126  * addition to other processes
1127  */
1128 int
1129 VLockFileLock(struct VLockFile *lf, afs_uint32 offset, int locktype, int nonblock)
1130 {
1131     int code;
1132
1133     opr_Assert(locktype == READ_LOCK || locktype == WRITE_LOCK);
1134
1135     MUTEX_ENTER(&lf->mutex);
1136
1137     if (lf->fd == INVALID_FD) {
1138         lf->fd = _VOpenPath(lf->path);
1139         if (lf->fd == INVALID_FD) {
1140             MUTEX_EXIT(&lf->mutex);
1141             return EIO;
1142         }
1143     }
1144
1145     lf->refcount++;
1146
1147     MUTEX_EXIT(&lf->mutex);
1148
1149     code = _VLockFd(lf->fd, offset, locktype, nonblock);
1150
1151     if (code) {
1152         MUTEX_ENTER(&lf->mutex);
1153         if (--lf->refcount < 1) {
1154             _VCloseFd(lf->fd);
1155             lf->fd = INVALID_FD;
1156         }
1157         MUTEX_EXIT(&lf->mutex);
1158     }
1159
1160     return code;
1161 }
1162
1163 void
1164 VLockFileUnlock(struct VLockFile *lf, afs_uint32 offset)
1165 {
1166     MUTEX_ENTER(&lf->mutex);
1167
1168     opr_Assert(lf->fd != INVALID_FD);
1169
1170     if (--lf->refcount < 1) {
1171         _VCloseFd(lf->fd);
1172         lf->fd = INVALID_FD;
1173     } else {
1174         _VUnlockFd(lf->fd, offset);
1175     }
1176
1177     MUTEX_EXIT(&lf->mutex);
1178 }
1179
1180 #ifdef AFS_DEMAND_ATTACH_FS
1181
1182 /**
1183  * initialize a struct VDiskLock.
1184  *
1185  * @param[in] dl struct VDiskLock to initialize
1186  * @param[in] lf the struct VLockFile to associate with this disk lock
1187  */
1188 void
1189 VDiskLockInit(struct VDiskLock *dl, struct VLockFile *lf, afs_uint32 offset)
1190 {
1191     opr_Assert(lf);
1192     memset(dl, 0, sizeof(*dl));
1193     Lock_Init(&dl->rwlock);
1194     MUTEX_INIT(&dl->mutex, "disklock", MUTEX_DEFAULT, 0);
1195     CV_INIT(&dl->cv, "disklock cv", CV_DEFAULT, 0);
1196     dl->lockfile = lf;
1197     dl->offset = offset;
1198 }
1199
1200 /**
1201  * acquire a lock on a file on local disk.
1202  *
1203  * @param[in] dl       the VDiskLock structure corresponding to the file on disk
1204  * @param[in] locktype READ_LOCK if you want a read lock, or WRITE_LOCK if
1205  *                     you want a write lock
1206  * @param[in] nonblock 0 to wait for conflicting locks to clear before
1207  *                     obtaining the lock; 1 to fail immediately if a
1208  *                     conflicting lock is held by someone else
1209  *
1210  * @return operation status
1211  *  @retval 0 success
1212  *  @retval EBUSY someone else is holding a conflicting lock and nonblock=1 was
1213  *                specified
1214  *  @retval EIO   error acquiring file lock
1215  *
1216  * @note DAFS only
1217  *
1218  * @note while normal fcntl-y locks on Unix systems generally only work per-
1219  * process, this interface also deals with locks between threads in the
1220  * process in addition to different processes acquiring the lock
1221  */
1222 int
1223 VGetDiskLock(struct VDiskLock *dl, int locktype, int nonblock)
1224 {
1225     int code = 0;
1226     opr_Assert(locktype == READ_LOCK || locktype == WRITE_LOCK);
1227
1228     if (nonblock) {
1229         if (locktype == READ_LOCK) {
1230             ObtainReadLockNoBlock(&dl->rwlock, code);
1231         } else {
1232             ObtainWriteLockNoBlock(&dl->rwlock, code);
1233         }
1234
1235         if (code) {
1236             return EBUSY;
1237         }
1238
1239     } else if (locktype == READ_LOCK) {
1240         ObtainReadLock(&dl->rwlock);
1241     } else {
1242         ObtainWriteLock(&dl->rwlock);
1243     }
1244
1245     MUTEX_ENTER(&dl->mutex);
1246
1247     if ((dl->flags & VDISKLOCK_ACQUIRING)) {
1248         /* Some other thread is waiting to acquire an fs lock. If nonblock=1,
1249          * we can return immediately, since we know we'll need to wait to
1250          * acquire. Otherwise, wait for the other thread to finish acquiring
1251          * the fs lock */
1252         if (nonblock) {
1253             code = EBUSY;
1254         } else {
1255             while ((dl->flags & VDISKLOCK_ACQUIRING)) {
1256                 CV_WAIT(&dl->cv, &dl->mutex);
1257             }
1258         }
1259     }
1260
1261     if (code == 0 && !(dl->flags & VDISKLOCK_ACQUIRED)) {
1262         /* no other thread holds the lock on the actual file; so grab one */
1263
1264         /* first try, don't block on the lock to see if we can get it without
1265          * waiting */
1266         code = VLockFileLock(dl->lockfile, dl->offset, locktype, 1);
1267
1268         if (code == EBUSY && !nonblock) {
1269
1270             /* mark that we are waiting on the fs lock */
1271             dl->flags |= VDISKLOCK_ACQUIRING;
1272
1273             MUTEX_EXIT(&dl->mutex);
1274             code = VLockFileLock(dl->lockfile, dl->offset, locktype, nonblock);
1275             MUTEX_ENTER(&dl->mutex);
1276
1277             dl->flags &= ~VDISKLOCK_ACQUIRING;
1278
1279             if (code == 0) {
1280                 dl->flags |= VDISKLOCK_ACQUIRED;
1281             }
1282
1283             CV_BROADCAST(&dl->cv);
1284         }
1285     }
1286
1287     if (code) {
1288         if (locktype == READ_LOCK) {
1289             ReleaseReadLock(&dl->rwlock);
1290         } else {
1291             ReleaseWriteLock(&dl->rwlock);
1292         }
1293     } else {
1294         /* successfully got the lock, so inc the number of unlocks we need
1295          * to do before we can unlock the actual file */
1296         ++dl->lockers;
1297     }
1298
1299     MUTEX_EXIT(&dl->mutex);
1300
1301     return code;
1302 }
1303
1304 /**
1305  * release a lock on a file on local disk.
1306  *
1307  * @param[in] dl the struct VDiskLock to release
1308  * @param[in] locktype READ_LOCK if you are unlocking a read lock, or
1309  *                     WRITE_LOCK if you are unlocking a write lock
1310  *
1311  * @return operation status
1312  *  @retval 0 success
1313  */
1314 void
1315 VReleaseDiskLock(struct VDiskLock *dl, int locktype)
1316 {
1317     opr_Assert(locktype == READ_LOCK || locktype == WRITE_LOCK);
1318
1319     MUTEX_ENTER(&dl->mutex);
1320     opr_Assert(dl->lockers > 0);
1321
1322     if (--dl->lockers < 1) {
1323         /* no threads are holding this lock anymore, so we can release the
1324          * actual disk lock */
1325         VLockFileUnlock(dl->lockfile, dl->offset);
1326         dl->flags &= ~VDISKLOCK_ACQUIRED;
1327     }
1328
1329     MUTEX_EXIT(&dl->mutex);
1330
1331     if (locktype == READ_LOCK) {
1332         ReleaseReadLock(&dl->rwlock);
1333     } else {
1334         ReleaseWriteLock(&dl->rwlock);
1335     }
1336 }
1337
1338 #endif /* AFS_DEMAND_ATTACH_FS */