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