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