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