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