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