0c0269623a3863c51869afb7a87c4f0f3919470d
[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_PTHREAD_ENV
841 # define AFS_LF_LOCK(lf) assert(pthread_mutex_lock(&((lf)->mutex)) == 0)
842 # define AFS_LF_UNLOCK(lf) assert(pthread_mutex_unlock(&((lf)->mutex)) == 0)
843 #else
844 # define AFS_LF_LOCK(lf)
845 # define AFS_LF_UNLOCK(lf)
846 #endif /* AFS_PTHREAD_ENV */
847
848 /**
849  * initialize a struct VLockFile.
850  *
851  * @param[in] lf   struct VLockFile to initialize
852  * @param[in] path Full path to the file to use for locks. The string contents
853  *                 are copied.
854  */
855 void
856 VLockFileInit(struct VLockFile *lf, const char *path)
857 {
858     memset(lf, 0, sizeof(*lf));
859     lf->path = strdup(path);
860     lf->fd = INVALID_FD;
861 #ifdef AFS_PTHREAD_ENV
862     assert(pthread_mutex_init(&lf->mutex, NULL) == 0);
863 #endif /* AFS_PTHREAD_ENV */
864 }
865
866 #ifdef AFS_NT40_ENV
867 static_inline FD_t
868 _VOpenPath(const char *path)
869 {
870     HANDLE handle;
871
872     handle = CreateFile(path,
873                         GENERIC_READ | GENERIC_WRITE,
874                         FILE_SHARE_READ | FILE_SHARE_WRITE,
875                         NULL,
876                         OPEN_ALWAYS,
877                         FILE_ATTRIBUTE_HIDDEN,
878                         NULL);
879     if (handle == INVALID_HANDLE_VALUE) {
880         return INVALID_FD;
881     }
882
883     return handle;
884 }
885
886 static_inline int
887 _VLockFd(FD_t handle, afs_uint32 offset, int locktype, int nonblock)
888 {
889     DWORD flags = 0;
890     OVERLAPPED lap;
891
892     if (locktype == WRITE_LOCK) {
893         flags |= LOCKFILE_EXCLUSIVE_LOCK;
894     }
895     if (nonblock) {
896         flags |= LOCKFILE_FAIL_IMMEDIATELY;
897     }
898
899     memset(&lap, 0, sizeof(lap));
900     lap.Offset = offset;
901
902     if (!LockFileEx(handle, flags, 0, 1, 0, &lap)) {
903         if (GetLastError() == ERROR_LOCK_VIOLATION) {
904             return EBUSY;
905         }
906         return EIO;
907     }
908
909     return 0;
910 }
911
912 static_inline void
913 _VUnlockFd(struct VLockFile *lf, afs_uint32 offset)
914 {
915     OVERLAPPED lap;
916
917     memset(&lap, 0, sizeof(lap));
918     lap.Offset = offset;
919
920     UnlockFileEx(lf->fd, 0, 1, 0, &lap);
921 }
922
923 static_inline void
924 _VCloseFd(struct VLockFile *lf)
925 {
926     CloseHandle(lf->fd);
927 }
928
929 #else /* !AFS_NT40_ENV */
930
931 /**
932  * open a file on the local filesystem suitable for locking
933  *
934  * @param[in] path  abs path of the file to open
935  *
936  * @return file descriptor
937  *  @retval INVALID_FD failure opening file
938  */
939 static_inline int
940 _VOpenPath(const char *path)
941 {
942     int fd;
943
944     fd = open(path, O_RDWR | O_CREAT, 0660);
945     if (fd < 0) {
946         return INVALID_FD;
947     }
948     return fd;
949 }
950
951 /**
952  * lock an offset in a file descriptor.
953  *
954  * @param[in] fd       file descriptor to lock
955  * @param[in] offset   offset in file to lock
956  * @param[in] locktype READ_LOCK or WRITE_LOCK
957  * @param[in] nonblock 1 to fail immediately, 0 to wait to acquire lock
958  *
959  * @return operation status
960  *  @retval 0 success
961  *  @retval EBUSY someone else is holding a conflicting lock and nonblock=1 was
962  *                specified
963  *  @retval EIO   error acquiring file lock
964  */
965 static_inline int
966 _VLockFd(int fd, afs_uint32 offset, int locktype, int nonblock)
967 {
968     int l_type = F_WRLCK;
969     int cmd = F_SETLKW;
970     struct flock sf;
971
972     if (locktype == READ_LOCK) {
973         l_type = F_RDLCK;
974     }
975     if (nonblock) {
976         cmd = F_SETLK;
977     }
978
979     sf.l_start = offset;
980     sf.l_len = 1;
981     sf.l_type = l_type;
982     sf.l_whence = SEEK_SET;
983
984     if (fcntl(fd, cmd, &sf)) {
985         if (nonblock && (errno == EACCES || errno == EAGAIN)) {
986             /* We asked for a nonblocking lock, and it was already locked */
987             return EBUSY;
988         }
989         Log("_VLockFd: fcntl failed with error %d when trying to lock "
990             "fd %d (locktype=%d)\n", errno, fd, locktype);
991         return EIO;
992     }
993
994     return 0;
995 }
996
997 /**
998  * close a file descriptor used for file locking.
999  *
1000  * @param[in] fd file descriptor to close
1001  */
1002 static_inline void
1003 _VCloseFd(int fd)
1004 {
1005     if (close(fd)) {
1006         Log("_VCloseFd: error %d closing fd %d\n",
1007             errno, fd);
1008     }
1009 }
1010
1011 /**
1012  * unlock a file offset in a file descriptor.
1013  *
1014  * @param[in] fd file descriptor to unlock
1015  * @param[in] offset offset to unlock
1016  */
1017 static_inline void
1018 _VUnlockFd(int fd, afs_uint32 offset)
1019 {
1020     struct flock sf;
1021
1022     sf.l_start = offset;
1023     sf.l_len = 1;
1024     sf.l_type = F_UNLCK;
1025     sf.l_whence = SEEK_SET;
1026
1027     if (fcntl(fd, F_SETLK, &sf)) {
1028         Log("_VUnlockFd: fcntl failed with error %d when trying to unlock "
1029             "fd %d\n", errno, fd);
1030     }
1031 }
1032 #endif /* !AFS_NT40_ENV */
1033
1034 /**
1035  * lock a file on disk for the process.
1036  *
1037  * @param[in] lf       the struct VLockFile representing the file to lock
1038  * @param[in] offset   the offset in the file to lock
1039  * @param[in] locktype READ_LOCK or WRITE_LOCK
1040  * @param[in] nonblock 0 to wait for conflicting locks to clear before
1041  *                     obtaining the lock; 1 to fail immediately if a
1042  *                     conflicting lock is held by someone else
1043  *
1044  * @return operation status
1045  *  @retval 0 success
1046  *  @retval EBUSY someone else is holding a conflicting lock and nonblock=1 was
1047  *                specified
1048  *  @retval EIO   error acquiring file lock
1049  *
1050  * @note DAFS only
1051  *
1052  * @note do not try to lock/unlock the same offset in the same file from
1053  * different threads; use VGetDiskLock to protect threads from each other in
1054  * addition to other processes
1055  */
1056 int
1057 VLockFileLock(struct VLockFile *lf, afs_uint32 offset, int locktype, int nonblock)
1058 {
1059     int code;
1060
1061     AFS_LF_LOCK(lf);
1062
1063     if (lf->fd == INVALID_FD) {
1064         lf->fd = _VOpenPath(lf->path);
1065         if (lf->fd == INVALID_FD) {
1066             AFS_LF_UNLOCK(lf);
1067             return EIO;
1068         }
1069     }
1070
1071     lf->refcount++;
1072
1073     AFS_LF_UNLOCK(lf);
1074
1075     code = _VLockFd(lf->fd, offset, locktype, nonblock);
1076
1077     if (code) {
1078         AFS_LF_LOCK(lf);
1079         if (--lf->refcount < 1) {
1080             _VCloseFd(lf->fd);
1081             lf->fd = INVALID_FD;
1082         }
1083         AFS_LF_UNLOCK(lf);
1084     }
1085
1086     return code;
1087 }
1088
1089 void
1090 VLockFileUnlock(struct VLockFile *lf, afs_uint32 offset)
1091 {
1092     AFS_LF_LOCK(lf);
1093
1094     if (--lf->refcount < 1) {
1095         _VCloseFd(lf->fd);
1096         lf->fd = INVALID_FD;
1097     } else {
1098         _VUnlockFd(lf->fd, offset);
1099     }
1100
1101     AFS_LF_UNLOCK(lf);
1102 }
1103
1104 #ifdef AFS_DEMAND_ATTACH_FS
1105
1106 /**
1107  * initialize a struct VDiskLock.
1108  *
1109  * @param[in] dl struct VDiskLock to initialize
1110  * @param[in] lf the struct VLockFile to associate with this disk lock
1111  */
1112 void
1113 VDiskLockInit(struct VDiskLock *dl, struct VLockFile *lf, afs_uint32 offset)
1114 {
1115     assert(lf);
1116     memset(dl, 0, sizeof(*dl));
1117     Lock_Init(&dl->rwlock);
1118     assert(pthread_mutex_init(&dl->mutex, NULL) == 0);
1119     assert(pthread_cond_init(&dl->cv, NULL) == 0);
1120     dl->lockfile = lf;
1121     dl->offset = offset;
1122 }
1123
1124 /**
1125  * acquire a lock on a file on local disk.
1126  *
1127  * @param[in] dl       the VDiskLock structure corresponding to the file on disk
1128  * @param[in] locktype READ_LOCK if you want a read lock, or WRITE_LOCK if
1129  *                     you want a write lock
1130  * @param[in] nonblock 0 to wait for conflicting locks to clear before
1131  *                     obtaining the lock; 1 to fail immediately if a
1132  *                     conflicting lock is held by someone else
1133  *
1134  * @return operation status
1135  *  @retval 0 success
1136  *  @retval EBUSY someone else is holding a conflicting lock and nonblock=1 was
1137  *                specified
1138  *  @retval EIO   error acquiring file lock
1139  *
1140  * @note DAFS only
1141  *
1142  * @note while normal fcntl-y locks on Unix systems generally only work per-
1143  * process, this interface also deals with locks between threads in the
1144  * process in addition to different processes acquiring the lock
1145  */
1146 int
1147 VGetDiskLock(struct VDiskLock *dl, int locktype, int nonblock)
1148 {
1149     int code = 0;
1150     assert(locktype == READ_LOCK || locktype == WRITE_LOCK);
1151
1152     if (nonblock) {
1153         if (locktype == READ_LOCK) {
1154             ObtainReadLockNoBlock(&dl->rwlock, code);
1155         } else {
1156             ObtainWriteLockNoBlock(&dl->rwlock, code);
1157         }
1158
1159         if (code) {
1160             return EBUSY;
1161         }
1162
1163     } else if (locktype == READ_LOCK) {
1164         ObtainReadLock(&dl->rwlock);
1165     } else {
1166         ObtainWriteLock(&dl->rwlock);
1167     }
1168
1169     assert(pthread_mutex_lock(&dl->mutex) == 0);
1170
1171     if ((dl->flags & VDISKLOCK_ACQUIRING)) {
1172         /* Some other thread is waiting to acquire an fs lock. If nonblock=1,
1173          * we can return immediately, since we know we'll need to wait to
1174          * acquire. Otherwise, wait for the other thread to finish acquiring
1175          * the fs lock */
1176         if (nonblock) {
1177             code = EBUSY;
1178         } else {
1179             while ((dl->flags & VDISKLOCK_ACQUIRING)) {
1180                 assert(pthread_cond_wait(&dl->cv, &dl->mutex) == 0);
1181             }
1182         }
1183     }
1184
1185     if (code == 0 && !(dl->flags & VDISKLOCK_ACQUIRED)) {
1186         /* no other thread holds the lock on the actual file; so grab one */
1187
1188         /* first try, don't block on the lock to see if we can get it without
1189          * waiting */
1190         code = VLockFileLock(dl->lockfile, dl->offset, locktype, 1);
1191
1192         if (code == EBUSY && !nonblock) {
1193
1194             /* mark that we are waiting on the fs lock */
1195             dl->flags |= VDISKLOCK_ACQUIRING;
1196
1197             assert(pthread_mutex_unlock(&dl->mutex) == 0);
1198             code = VLockFileLock(dl->lockfile, dl->offset, locktype, nonblock);
1199             assert(pthread_mutex_lock(&dl->mutex) == 0);
1200
1201             dl->flags &= ~VDISKLOCK_ACQUIRING;
1202
1203             if (code == 0) {
1204                 dl->flags |= VDISKLOCK_ACQUIRED;
1205             }
1206
1207             assert(pthread_cond_broadcast(&dl->cv) == 0);
1208         }
1209     }
1210
1211     if (code) {
1212         if (locktype == READ_LOCK) {
1213             ReleaseReadLock(&dl->rwlock);
1214         } else {
1215             ReleaseWriteLock(&dl->rwlock);
1216         }
1217     } else {
1218         /* successfully got the lock, so inc the number of unlocks we need
1219          * to do before we can unlock the actual file */
1220         ++dl->lockers;
1221     }
1222
1223     assert(pthread_mutex_unlock(&dl->mutex) == 0);
1224
1225     return code;
1226 }
1227
1228 /**
1229  * release a lock on a file on local disk.
1230  *
1231  * @param[in] dl the struct VDiskLock to release
1232  * @param[in] locktype READ_LOCK if you are unlocking a read lock, or
1233  *                     WRITE_LOCK if you are unlocking a write lock
1234  *
1235  * @return operation status
1236  *  @retval 0 success
1237  */
1238 void
1239 VReleaseDiskLock(struct VDiskLock *dl, int locktype)
1240 {
1241     assert(locktype == READ_LOCK || locktype == WRITE_LOCK);
1242
1243     assert(pthread_mutex_lock(&dl->mutex) == 0);
1244     assert(dl->lockers > 0);
1245
1246     if (--dl->lockers < 1) {
1247         /* no threads are holding this lock anymore, so we can release the
1248          * actual disk lock */
1249         VLockFileUnlock(dl->lockfile, dl->offset);
1250         dl->flags &= ~VDISKLOCK_ACQUIRED;
1251     }
1252
1253     assert(pthread_mutex_unlock(&dl->mutex) == 0);
1254
1255     if (locktype == READ_LOCK) {
1256         ReleaseReadLock(&dl->rwlock);
1257     } else {
1258         ReleaseWriteLock(&dl->rwlock);
1259     }
1260 }
1261
1262 #endif /* AFS_DEMAND_ATTACH_FS */