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