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