2 * Copyright 2000, International Business Machines Corporation and others.
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
13 Institution: The Information Technology Center, Carnegie-Mellon University
17 #include <afsconfig.h>
18 #include <afs/param.h>
22 #include <sys/types.h>
34 #include <afs/afs_assert.h>
37 #include <afs/afsint.h>
39 #include <afs/errors.h>
42 #include <afs/afssyscalls.h>
44 #include <afs/afsutil.h>
51 #include "volume_inline.h"
52 #include "partition.h"
53 #include "viceinode.h"
55 #include "volinodes.h"
56 #include "vol_prototypes.h"
60 #include <sys/lockf.h>
62 #if defined(AFS_SUN5_ENV) || defined(AFS_NT40_ENV) || defined(AFS_LINUX20_ENV)
69 #define afs_open open64
70 #else /* !O_LARGEFILE */
72 #endif /* !O_LARGEFILE */
77 # define AFS_SETLKW F_SETLKW64
78 # define AFS_SETLK F_SETLK64
79 # define afs_st_flock flock64
81 # define AFS_SETLKW F_SETLKW
82 # define AFS_SETLK F_SETLK
83 # define afs_st_flock flock
87 /* Note: the volume creation functions herein leave the destroyMe flag in the
88 volume header ON: this means that the volumes will not be attached by the
89 file server and WILL BE DESTROYED the next time a system salvage is performed */
91 #ifdef FSSYNC_BUILD_CLIENT
93 RemoveInodes(struct afs_inode_info
*stuff
, Device dev
, VolumeId parent
,
99 /* This relies on the fact that IDEC only needs the device and NT only
100 * needs the dev and vid to decrement volume special files.
102 IH_INIT(handle
, dev
, parent
, -1);
103 for (i
= 0; i
< MAXINODETYPE
; i
++) {
104 Inode inode
= *stuff
[i
].inode
;
105 if (VALID_INO(inode
)) {
106 if (stuff
[i
].inodeType
== VI_LINKTABLE
) {
107 IH_DEC(handle
, inode
, parent
);
109 IH_DEC(handle
, inode
, vid
);
117 VCreateVolume(Error
* ec
, char *partname
, VolId volumeId
, VolId parentId
)
118 { /* Should be the same as volumeId if there is
122 retVal
= VCreateVolume_r(ec
, partname
, volumeId
, parentId
);
128 VCreateVolume_r(Error
* ec
, char *partname
, VolId volumeId
, VolId parentId
)
129 { /* Should be the same as volumeId if there is
133 char headerName
[VMAXPATHLEN
], volumePath
[VMAXPATHLEN
];
135 struct DiskPartition64
*partition
;
136 struct VolumeDiskHeader diskHeader
;
139 Inode nearInode AFS_UNUSED
= 0;
143 struct VolumeHeader tempHeader
;
144 struct afs_inode_info stuff
[MAXINODETYPE
];
145 # ifdef AFS_DEMAND_ATTACH_FS
147 # endif /* AFS_DEMAND_ATTACH_FS */
149 init_inode_info(&tempHeader
, stuff
);
152 memset(&vol
, 0, sizeof(vol
));
154 vol
.parentId
= parentId
;
155 vol
.copyDate
= time(0); /* The only date which really means when this
156 * @i(instance) of this volume was created.
157 * Creation date does not mean this */
159 /* Initialize handle for error case below. */
162 /* Verify that the parition is valid before writing to it. */
163 if (!(partition
= VGetPartition_r(partname
, 0))) {
164 Log("VCreateVolume: partition %s is not in service.\n", partname
);
168 #if defined(NEARINODE_HINT)
169 nearInodeHash(volumeId
, nearInode
);
170 nearInode
%= partition
->f_files
;
172 VGetVolumePath(ec
, vol
.id
, &part
, &name
);
173 if (*ec
== VNOVOL
|| !strcmp(partition
->name
, part
)) {
174 /* this case is ok */
176 /* return EXDEV if it's a clone or read-only to an alternate partition
177 * otherwise assume it's a move */
178 if (vol
.parentId
!= vol
.id
) {
179 Log("VCreateVolume: volume %lu for parent %lu"
180 " found on %s; unable to create volume on %s.\n",
181 afs_printable_uint32_lu(vol
.id
),
182 afs_printable_uint32_lu(vol
.parentId
), part
, partition
->name
);
189 # ifdef AFS_DEMAND_ATTACH_FS
190 /* volume doesn't exist yet, but we must lock it to try to prevent something
191 * else from reading it when we're e.g. half way through creating it (or
192 * something tries to create the same volume at the same time) */
193 locktype
= VVolLockType(V_VOLUPD
, 1);
194 rc
= VLockVolumeByIdNB(volumeId
, partition
, locktype
);
196 Log("VCreateVolume: vol %lu already locked by someone else\n",
197 afs_printable_uint32_lu(volumeId
));
201 # else /* AFS_DEMAND_ATTACH_FS */
202 VLockPartition_r(partname
);
203 # endif /* !AFS_DEMAND_ATTACH_FS */
205 memset(&tempHeader
, 0, sizeof(tempHeader
));
206 tempHeader
.stamp
.magic
= VOLUMEHEADERMAGIC
;
207 tempHeader
.stamp
.version
= VOLUMEHEADERVERSION
;
208 tempHeader
.id
= vol
.id
;
209 tempHeader
.parent
= vol
.parentId
;
210 vol
.stamp
.magic
= VOLUMEINFOMAGIC
;
211 vol
.stamp
.version
= VOLUMEINFOVERSION
;
212 vol
.destroyMe
= DESTROY_ME
;
213 (void)afs_snprintf(headerName
, sizeof headerName
, VFORMAT
, afs_printable_uint32_lu(vol
.id
));
214 (void)afs_snprintf(volumePath
, sizeof volumePath
, "%s" OS_DIRSEP
"%s",
215 VPartitionPath(partition
), headerName
);
216 rc
= stat(volumePath
, &st
);
217 if (rc
== 0 || errno
!= ENOENT
) {
219 Log("VCreateVolume: Header file %s already exists!\n",
223 Log("VCreateVolume: Error %d trying to stat header file %s\n",
229 device
= partition
->device
;
231 for (i
= 0; i
< MAXINODETYPE
; i
++) {
232 struct afs_inode_info
*p
= &stuff
[i
];
237 IH_CREATE(NULL
, device
, VPartitionPath(partition
), nearInode
,
238 (p
->inodeType
== VI_LINKTABLE
) ? vol
.parentId
: vol
.id
,
239 INODESPECIAL
, p
->inodeType
, vol
.parentId
);
240 if (!(VALID_INO(*(p
->inode
)))) {
241 if (errno
== EEXIST
&& (p
->inodeType
== VI_LINKTABLE
)) {
242 /* Increment the reference count instead. */
246 *(p
->inode
) = namei_MakeSpecIno(vol
.parentId
, VI_LINKTABLE
);
247 IH_INIT(lh
, device
, parentId
, *(p
->inode
));
253 code
= IH_INC(lh
, *(p
->inode
), parentId
);
254 FDH_REALLYCLOSE(fdP
);
263 IH_CREATE(NULL
, device
, VPartitionPath(partition
), nearInode
,
264 vol
.id
, INODESPECIAL
, p
->inodeType
, vol
.parentId
);
267 if (!VALID_INO(*(p
->inode
))) {
268 Log("VCreateVolume: Problem creating %s file associated with volume header %s\n", p
->description
, volumePath
);
272 RemoveInodes(stuff
, device
, vol
.parentId
, vol
.id
);
276 VDestroyVolumeDiskHeader(partition
, volumeId
, parentId
);
278 # ifdef AFS_DEMAND_ATTACH_FS
280 VUnlockVolumeById(volumeId
, partition
);
282 # endif /* AFS_DEMAND_ATTACH_FS */
285 IH_INIT(handle
, device
, vol
.parentId
, *(p
->inode
));
286 fdP
= IH_OPEN(handle
);
288 Log("VCreateVolume: Problem iopen inode %s (err=%d)\n",
289 PrintInode(stmp
, *(p
->inode
)), errno
);
292 if (FDH_PWRITE(fdP
, (char *)&p
->stamp
, sizeof(p
->stamp
), 0) !=
294 Log("VCreateVolume: Problem writing to inode %s (err=%d)\n",
295 PrintInode(stmp
, *(p
->inode
)), errno
);
296 FDH_REALLYCLOSE(fdP
);
299 FDH_REALLYCLOSE(fdP
);
301 nearInode
= *(p
->inode
);
304 IH_INIT(handle
, device
, vol
.parentId
, tempHeader
.volumeInfo
);
305 fdP
= IH_OPEN(handle
);
307 Log("VCreateVolume: Problem iopen inode %s (err=%d)\n",
308 PrintInode(stmp
, tempHeader
.volumeInfo
), errno
);
311 if (FDH_PWRITE(fdP
, (char *)&vol
, sizeof(vol
), 0) != sizeof(vol
)) {
312 Log("VCreateVolume: Problem writing to inode %s (err=%d)\n",
313 PrintInode(stmp
, tempHeader
.volumeInfo
), errno
);
314 FDH_REALLYCLOSE(fdP
);
320 VolumeHeaderToDisk(&diskHeader
, &tempHeader
);
321 rc
= VCreateVolumeDiskHeader(&diskHeader
, partition
);
323 Log("VCreateVolume: Error %d trying to write volume header for "
324 "volume %u on partition %s; volume not created\n", rc
,
325 vol
.id
, VPartitionPath(partition
));
332 # ifdef AFS_DEMAND_ATTACH_FS
334 VUnlockVolumeById(volumeId
, partition
);
336 # endif /* AFS_DEMAND_ATTACH_FS */
337 return (VAttachVolumeByName_r(ec
, partname
, headerName
, V_SECRETLY
));
339 #endif /* FSSYNC_BUILD_CLIENT */
343 AssignVolumeName(VolumeDiskData
* vol
, char *name
, char *ext
)
346 AssignVolumeName_r(vol
, name
, ext
);
351 AssignVolumeName_r(VolumeDiskData
* vol
, char *name
, char *ext
)
354 strncpy(vol
->name
, name
, VNAMESIZE
- 1);
355 vol
->name
[VNAMESIZE
- 1] = '\0';
356 dot
= strrchr(vol
->name
, '.');
357 if (dot
&& (strcmp(dot
, ".backup") == 0 || strcmp(dot
, ".readonly") == 0))
360 strncat(vol
->name
, ext
, VNAMESIZE
- 1 - strlen(vol
->name
));
364 CopyVolumeHeader_r(VolumeDiskData
* from
, VolumeDiskData
* to
)
366 /* The id and parentId fields are not copied; these are inviolate--the to volume
367 * is assumed to have already been created. The id's cannot be changed once
368 * creation has taken place, since they are embedded in the various inodes associated
369 * with the volume. The copydate is also inviolate--it always reflects the time
370 * this volume was created (compare with the creation date--the creation date of
371 * a backup volume is the creation date of the original parent, because the backup
372 * is used to backup the parent volume). */
376 parent
= to
->parentId
;
377 copydate
= to
->copyDate
;
378 memcpy(to
, from
, sizeof(*from
));
380 to
->parentId
= parent
;
381 to
->copyDate
= copydate
;
382 to
->destroyMe
= DESTROY_ME
; /* Caller must always clear this!!! */
383 to
->stamp
.magic
= VOLUMEINFOMAGIC
;
384 to
->stamp
.version
= VOLUMEINFOVERSION
;
389 CopyVolumeHeader(VolumeDiskData
* from
, VolumeDiskData
* to
)
394 code
= CopyVolumeHeader_r(from
, to
);
400 ClearVolumeStats(VolumeDiskData
* vol
)
403 ClearVolumeStats_r(vol
);
408 ClearVolumeStats_r(VolumeDiskData
* vol
)
410 memset(vol
->weekUse
, 0, sizeof(vol
->weekUse
));
416 CopyVolumeStats_r(VolumeDiskData
* from
, VolumeDiskData
* to
)
418 memcpy(to
->weekUse
, from
->weekUse
, sizeof(to
->weekUse
));
419 to
->dayUse
= from
->dayUse
;
420 to
->dayUseDate
= from
->dayUseDate
;
421 if (from
->stat_initialized
) {
422 memcpy(to
->stat_reads
, from
->stat_reads
, sizeof(to
->stat_reads
));
423 memcpy(to
->stat_writes
, from
->stat_writes
, sizeof(to
->stat_writes
));
424 memcpy(to
->stat_fileSameAuthor
, from
->stat_fileSameAuthor
,
425 sizeof(to
->stat_fileSameAuthor
));
426 memcpy(to
->stat_fileDiffAuthor
, from
->stat_fileDiffAuthor
,
427 sizeof(to
->stat_fileDiffAuthor
));
428 memcpy(to
->stat_dirSameAuthor
, from
->stat_dirSameAuthor
,
429 sizeof(to
->stat_dirSameAuthor
));
430 memcpy(to
->stat_dirDiffAuthor
, from
->stat_dirDiffAuthor
,
431 sizeof(to
->stat_dirDiffAuthor
));
436 CopyVolumeStats(VolumeDiskData
* from
, VolumeDiskData
* to
)
439 CopyVolumeStats_r(from
, to
);
444 * read an existing volume disk header.
446 * @param[in] volid volume id
447 * @param[in] dp disk partition object
448 * @param[out] hdr volume disk header or NULL
450 * @note if hdr is NULL, this is essentially an existence test for the vol
453 * @return operation status
455 * @retval -1 volume header doesn't exist
456 * @retval EIO failed to read volume header
461 VReadVolumeDiskHeader(VolumeId volid
,
462 struct DiskPartition64
* dp
,
463 VolumeDiskHeader_t
* hdr
)
467 char path
[MAXPATHLEN
];
469 (void)afs_snprintf(path
, sizeof(path
),
470 "%s" OS_DIRSEP VFORMAT
,
471 VPartitionPath(dp
), afs_printable_uint32_lu(volid
));
472 fd
= open(path
, O_RDONLY
);
474 Log("VReadVolumeDiskHeader: Couldn't open header for volume %lu (errno %d).\n",
475 afs_printable_uint32_lu(volid
), errno
);
478 } else if (hdr
&& read(fd
, hdr
, sizeof(*hdr
)) != sizeof(*hdr
)) {
479 Log("VReadVolumeDiskHeader: Couldn't read header for volume %lu.\n",
480 afs_printable_uint32_lu(volid
));
490 #ifdef FSSYNC_BUILD_CLIENT
492 * write an existing volume disk header.
494 * @param[in] hdr volume disk header
495 * @param[in] dp disk partition object
496 * @param[in] cr assert if O_CREAT | O_EXCL should be passed to open()
498 * @return operation status
500 * @retval -1 volume header doesn't exist
501 * @retval EIO failed to write volume header
506 _VWriteVolumeDiskHeader(VolumeDiskHeader_t
* hdr
,
507 struct DiskPartition64
* dp
,
512 char path
[MAXPATHLEN
];
514 #ifdef AFS_DEMAND_ATTACH_FS
515 /* prevent racing with VGC scanners reading the vol header while we are
517 code
= VPartHeaderLock(dp
, READ_LOCK
);
521 #endif /* AFS_DEMAND_ATTACH_FS */
525 (void)afs_snprintf(path
, sizeof(path
),
526 "%s" OS_DIRSEP VFORMAT
,
527 VPartitionPath(dp
), afs_printable_uint32_lu(hdr
->id
));
528 fd
= open(path
, flags
, 0644);
531 Log("_VWriteVolumeDiskHeader: Couldn't open header for volume %lu, "
532 "error = %d\n", afs_printable_uint32_lu(hdr
->id
), errno
);
533 } else if (write(fd
, hdr
, sizeof(*hdr
)) != sizeof(*hdr
)) {
534 Log("_VWriteVolumeDiskHeader: Couldn't write header for volume %lu, "
535 "error = %d\n", afs_printable_uint32_lu(hdr
->id
), errno
);
540 if (close(fd
) != 0) {
541 Log("_VWriteVolumeDiskHeader: Error closing header for volume "
542 "%lu, errno %d\n", afs_printable_uint32_lu(hdr
->id
), errno
);
546 #ifdef AFS_DEMAND_ATTACH_FS
547 VPartHeaderUnlock(dp
, READ_LOCK
);
548 #endif /* AFS_DEMAND_ATTACH_FS */
554 * write an existing volume disk header.
556 * @param[in] hdr volume disk header
557 * @param[in] dp disk partition object
559 * @return operation status
561 * @retval ENOENT volume header doesn't exist
562 * @retval EIO failed to write volume header
565 VWriteVolumeDiskHeader(VolumeDiskHeader_t
* hdr
,
566 struct DiskPartition64
* dp
)
570 #ifdef AFS_DEMAND_ATTACH_FS
571 VolumeDiskHeader_t oldhdr
;
572 int delvgc
= 0, addvgc
= 0;
575 /* first, see if anything with the volume IDs have changed; if so, we
576 * need to update the VGC */
578 code
= VReadVolumeDiskHeader(hdr
->id
, dp
, &oldhdr
);
579 if (code
== 0 && (oldhdr
.id
!= hdr
->id
|| oldhdr
.parent
!= hdr
->parent
)) {
580 /* the vol id or parent vol id changed; need to delete the VGC entry
581 * for the old vol id/parent, and add the new one */
586 /* couldn't get the old header info; add the new header info to the
587 * VGC in case it hasn't been added yet */
591 #endif /* AFS_DEMAND_ATTACH_FS */
593 code
= _VWriteVolumeDiskHeader(hdr
, dp
, 0);
598 #ifdef AFS_DEMAND_ATTACH_FS
600 memset(&res
, 0, sizeof(res
));
601 code
= FSYNC_VGCDel(dp
->name
, oldhdr
.parent
, oldhdr
.id
, FSYNC_WHATEVER
, &res
);
603 /* unknown vol id is okay; it just further suggests the old header
604 * data was bogus, which is fine since we're trying to fix it */
605 if (code
&& res
.hdr
.reason
!= FSYNC_UNKNOWN_VOLID
) {
606 Log("VWriteVolumeDiskHeader: FSYNC_VGCDel(%s, %lu, %lu) "
607 "failed with code %ld reason %ld\n", dp
->name
,
608 afs_printable_uint32_lu(oldhdr
.parent
),
609 afs_printable_uint32_lu(oldhdr
.id
),
610 afs_printable_int32_ld(code
),
611 afs_printable_int32_ld(res
.hdr
.reason
));
616 memset(&res
, 0, sizeof(res
));
617 code
= FSYNC_VGCAdd(dp
->name
, hdr
->parent
, hdr
->id
, FSYNC_WHATEVER
, &res
);
619 Log("VWriteVolumeDiskHeader: FSYNC_VGCAdd(%s, %lu, %lu) "
620 "failed with code %ld reason %ld\n", dp
->name
,
621 afs_printable_uint32_lu(hdr
->parent
),
622 afs_printable_uint32_lu(hdr
->id
),
623 afs_printable_int32_ld(code
),
624 afs_printable_int32_ld(res
.hdr
.reason
));
628 #endif /* AFS_DEMAND_ATTACH_FS */
635 * create and write a volume disk header to disk.
637 * @param[in] hdr volume disk header
638 * @param[in] dp disk partition object
640 * @return operation status
642 * @retval EEXIST volume header already exists
643 * @retval EIO failed to write volume header
648 VCreateVolumeDiskHeader(VolumeDiskHeader_t
* hdr
,
649 struct DiskPartition64
* dp
)
652 #ifdef AFS_DEMAND_ATTACH_FS
654 #endif /* AFS_DEMAND_ATTACH_FS */
656 code
= _VWriteVolumeDiskHeader(hdr
, dp
, O_CREAT
| O_EXCL
);
661 #ifdef AFS_DEMAND_ATTACH_FS
662 memset(&res
, 0, sizeof(res
));
663 code
= FSYNC_VGCAdd(dp
->name
, hdr
->parent
, hdr
->id
, FSYNC_WHATEVER
, &res
);
665 Log("VCreateVolumeDiskHeader: FSYNC_VGCAdd(%s, %lu, %lu) failed "
666 "with code %ld reason %ld\n", dp
->name
,
667 afs_printable_uint32_lu(hdr
->parent
),
668 afs_printable_uint32_lu(hdr
->id
),
669 afs_printable_int32_ld(code
),
670 afs_printable_int32_ld(res
.hdr
.reason
));
672 #endif /* AFS_DEMAND_ATTACH_FS */
680 * destroy a volume disk header.
682 * @param[in] dp disk partition object
683 * @param[in] volid volume id
684 * @param[in] parent parent's volume id, 0 if unknown
686 * @return operation status
689 * @note if parent is 0, the parent volume ID will be looked up from the
692 * @note for non-DAFS, parent is currently ignored
695 VDestroyVolumeDiskHeader(struct DiskPartition64
* dp
,
700 char path
[MAXPATHLEN
];
701 #ifdef AFS_DEMAND_ATTACH_FS
703 #endif /* AFS_DEMAND_ATTACH_FS */
705 (void)afs_snprintf(path
, sizeof(path
),
706 "%s" OS_DIRSEP VFORMAT
,
707 VPartitionPath(dp
), afs_printable_uint32_lu(volid
));
710 Log("VDestroyVolumeDiskHeader: Couldn't unlink disk header, error = %d\n", errno
);
714 #ifdef AFS_DEMAND_ATTACH_FS
715 memset(&res
, 0, sizeof(res
));
717 FSSYNC_VGQry_response_t q_res
;
719 code
= FSYNC_VGCQuery(dp
->name
, volid
, &q_res
, &res
);
721 Log("VDestroyVolumeDiskHeader: FSYNC_VGCQuery(%s, %lu) failed "
722 "with code %ld, reason %ld\n", dp
->name
,
723 afs_printable_uint32_lu(volid
), afs_printable_int32_ld(code
),
724 afs_printable_int32_ld(res
.hdr
.reason
));
731 code
= FSYNC_VGCDel(dp
->name
, parent
, volid
, FSYNC_WHATEVER
, &res
);
733 Log("VDestroyVolumeDiskHeader: FSYNC_VGCDel(%s, %lu, %lu) failed "
734 "with code %ld reason %ld\n", dp
->name
,
735 afs_printable_uint32_lu(parent
),
736 afs_printable_uint32_lu(volid
),
737 afs_printable_int32_ld(code
),
738 afs_printable_int32_ld(res
.hdr
.reason
));
740 #endif /* AFS_DEMAND_ATTACH_FS */
745 #endif /* FSSYNC_BUILD_CLIENT */
748 * handle a single vol header as part of VWalkVolumeHeaders.
750 * @param[in] dp disk partition
751 * @param[in] volfunc function to call when a vol header is successfully read
752 * @param[in] name full path name to the .vol header
753 * @param[out] hdr header data read in from the .vol header
754 * @param[in] locked 1 if the partition headers are locked, 0 otherwise
755 * @param[in] rock the rock to pass to volfunc
757 * @return operation status
759 * @retval -1 fatal error, stop scanning
760 * @retval 1 failed to read header
761 * @retval 2 volfunc callback indicated error after header read
764 _VHandleVolumeHeader(struct DiskPartition64
*dp
, VWalkVolFunc volfunc
,
765 const char *name
, struct VolumeDiskHeader
*hdr
,
766 int locked
, void *rock
)
771 if ((fd
= afs_open(name
, O_RDONLY
)) == -1
772 || read(fd
, hdr
, sizeof(*hdr
))
774 || hdr
->stamp
.magic
!= VOLUMEHEADERMAGIC
) {
782 #ifdef AFSFS_DEMAND_ATTACH_FS
784 VPartHeaderUnlock(dp
);
786 #endif /* AFS_DEMAND_ATTACH_FS */
788 if (!error
&& volfunc
) {
789 /* the volume header seems fine; call the caller-supplied
790 * 'we-found-a-volume-header' function */
793 #ifdef AFS_DEMAND_ATTACH_FS
797 #endif /* AFS_DEMAND_ATTACH_FS */
799 error
= (*volfunc
) (dp
, name
, hdr
, last
, rock
);
808 #ifdef AFS_DEMAND_ATTACH_FS
809 if (error
&& !locked
) {
811 /* retry reading the volume header under the partition
812 * header lock, just to be safe and ensure we're not
813 * racing something rewriting the vol header */
814 code
= VPartHeaderLock(dp
, WRITE_LOCK
);
816 Log("Error acquiring partition write lock when "
817 "looking at header %s\n", name
);
821 return _VHandleVolumeHeader(dp
, volfunc
, name
, hdr
, 1, rock
);
823 #endif /* AFS_DEMAND_ATTACH_FS */
829 * walk through the list of volume headers on a partition.
831 * This function looks through all of the .vol headers on a partition, reads in
832 * each header, and calls the supplied volfunc function on each one. If the
833 * header cannot be read (or volfunc returns a positive error code), DAFS will
834 * VPartHeaderExLock() and retry. If that fails, or if we are non-DAFS, errfunc
835 * will be called (which typically will unlink the problem volume header).
837 * If volfunc returns a negative error code, walking the partition will stop
838 * and we will return an error immediately.
840 * @param[in] dp partition to walk
841 * @param[in] partpath the path opendir()
842 * @param[in] volfunc the function to call when a header is encountered, or
843 * NULL to just skip over valid headers
844 * @param[in] errfunc the function to call when a problematic header is
845 * encountered, or NULL to just skip over bad headers
846 * @param[in] rock rock for volfunc and errfunc
851 * @return operation status
853 * @retval negative fatal error, walk did not finish
856 VWalkVolumeHeaders(struct DiskPartition64
*dp
, const char *partpath
,
857 VWalkVolFunc volfunc
, VWalkErrFunc errfunc
, void *rock
)
860 struct dirent
*dentry
= NULL
;
862 struct VolumeDiskHeader diskHeader
;
864 dirp
= opendir(partpath
);
866 Log("VWalkVolumeHeaders: cannot open directory %s\n", partpath
);
871 while ((dentry
= readdir(dirp
))) {
872 char *p
= dentry
->d_name
;
873 p
= strrchr(dentry
->d_name
, '.');
874 if (p
!= NULL
&& strcmp(p
, VHDREXT
) == 0) {
875 char name
[VMAXPATHLEN
];
877 sprintf(name
, "%s" OS_DIRSEP
"%s", partpath
, dentry
->d_name
);
879 code
= _VHandleVolumeHeader(dp
, volfunc
, name
, &diskHeader
, -1, rock
);
881 /* fatal error, stop walking */
884 if (code
&& errfunc
) {
885 /* error with header; call the caller-supplied vol error
888 struct VolumeDiskHeader
*hdr
= &diskHeader
;
890 /* we failed to read the header at all, so don't pass in
894 (*errfunc
) (dp
, name
, hdr
, rock
);
909 * initialize a struct VLockFile.
911 * @param[in] lf struct VLockFile to initialize
912 * @param[in] path Full path to the file to use for locks. The string contents
916 VLockFileInit(struct VLockFile
*lf
, const char *path
)
918 memset(lf
, 0, sizeof(*lf
));
919 lf
->path
= strdup(path
);
921 MUTEX_INIT(&lf
->mutex
, "vlockfile", MUTEX_DEFAULT
, 0);
926 _VOpenPath(const char *path
)
930 handle
= CreateFile(path
,
931 GENERIC_READ
| GENERIC_WRITE
,
932 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
935 FILE_ATTRIBUTE_HIDDEN
,
937 if (handle
== INVALID_HANDLE_VALUE
) {
945 _VLockFd(FD_t handle
, afs_uint32 offset
, int locktype
, int nonblock
)
950 if (locktype
== WRITE_LOCK
) {
951 flags
|= LOCKFILE_EXCLUSIVE_LOCK
;
954 flags
|= LOCKFILE_FAIL_IMMEDIATELY
;
957 memset(&lap
, 0, sizeof(lap
));
960 if (!LockFileEx(handle
, flags
, 0, 1, 0, &lap
)) {
961 if (GetLastError() == ERROR_LOCK_VIOLATION
) {
971 _VUnlockFd(struct VLockFile
*lf
, afs_uint32 offset
)
975 memset(&lap
, 0, sizeof(lap
));
978 UnlockFileEx(lf
->fd
, 0, 1, 0, &lap
);
982 _VCloseFd(struct VLockFile
*lf
)
987 #else /* !AFS_NT40_ENV */
990 * open a file on the local filesystem suitable for locking
992 * @param[in] path abs path of the file to open
994 * @return file descriptor
995 * @retval INVALID_FD failure opening file
998 _VOpenPath(const char *path
)
1002 fd
= open(path
, O_RDWR
| O_CREAT
, 0660);
1010 * lock an offset in a file descriptor.
1012 * @param[in] fd file descriptor to lock
1013 * @param[in] offset offset in file to lock
1014 * @param[in] locktype READ_LOCK or WRITE_LOCK
1015 * @param[in] nonblock 1 to fail immediately, 0 to wait to acquire lock
1017 * @return operation status
1019 * @retval EBUSY someone else is holding a conflicting lock and nonblock=1 was
1021 * @retval EIO error acquiring file lock
1024 _VLockFd(int fd
, afs_uint32 offset
, int locktype
, int nonblock
)
1026 int l_type
= F_WRLCK
;
1027 int cmd
= AFS_SETLKW
;
1028 struct afs_st_flock sf
;
1030 if (locktype
== READ_LOCK
) {
1037 sf
.l_start
= offset
;
1040 sf
.l_whence
= SEEK_SET
;
1042 if (fcntl(fd
, cmd
, &sf
)) {
1043 if (nonblock
&& (errno
== EACCES
|| errno
== EAGAIN
)) {
1044 /* We asked for a nonblocking lock, and it was already locked */
1046 if (fcntl(fd
, F_GETLK
, &sf
) != 0 || sf
.l_pid
== 0) {
1047 Log("_VLockFd: fcntl failed with error %d when trying to "
1048 "query the conflicting lock for fd %d (locktype=%d, "
1049 "offset=%lu)\n", errno
, fd
, locktype
,
1050 afs_printable_uint32_lu(offset
));
1052 Log("_VLockFd: conflicting lock held on fd %d, offset %lu by "
1053 "pid %ld (locktype=%d)\n", fd
,
1054 afs_printable_uint32_lu(offset
), (long int)sf
.l_pid
,
1059 Log("_VLockFd: fcntl failed with error %d when trying to lock "
1060 "fd %d (locktype=%d, offset=%lu)\n", errno
, fd
, locktype
,
1061 afs_printable_uint32_lu(offset
));
1069 * close a file descriptor used for file locking.
1071 * @param[in] fd file descriptor to close
1077 Log("_VCloseFd: error %d closing fd %d\n",
1083 * unlock a file offset in a file descriptor.
1085 * @param[in] fd file descriptor to unlock
1086 * @param[in] offset offset to unlock
1089 _VUnlockFd(int fd
, afs_uint32 offset
)
1091 struct afs_st_flock sf
;
1093 sf
.l_start
= offset
;
1095 sf
.l_type
= F_UNLCK
;
1096 sf
.l_whence
= SEEK_SET
;
1098 if (fcntl(fd
, AFS_SETLK
, &sf
)) {
1099 Log("_VUnlockFd: fcntl failed with error %d when trying to unlock "
1100 "fd %d\n", errno
, fd
);
1103 #endif /* !AFS_NT40_ENV */
1106 * reinitialize a struct VLockFile.
1108 * Use this to close the lock file (unlocking any locks in it), and effectively
1109 * restore lf to the state it was in when it was initialized. This is the same
1110 * as unlocking all of the locks on the file, without having to remember what
1111 * all of the locks were. Do not unlock previously held locks after calling
1114 * @param[in] lf struct VLockFile to reinit
1116 * @pre nobody is waiting for a lock on this lockfile or otherwise using
1117 * this lockfile at all
1120 VLockFileReinit(struct VLockFile
*lf
)
1122 MUTEX_ENTER(&lf
->mutex
);
1124 if (lf
->fd
!= INVALID_FD
) {
1126 lf
->fd
= INVALID_FD
;
1131 MUTEX_EXIT(&lf
->mutex
);
1135 * lock a file on disk for the process.
1137 * @param[in] lf the struct VLockFile representing the file to lock
1138 * @param[in] offset the offset in the file to lock
1139 * @param[in] locktype READ_LOCK or WRITE_LOCK
1140 * @param[in] nonblock 0 to wait for conflicting locks to clear before
1141 * obtaining the lock; 1 to fail immediately if a
1142 * conflicting lock is held by someone else
1144 * @return operation status
1146 * @retval EBUSY someone else is holding a conflicting lock and nonblock=1 was
1148 * @retval EIO error acquiring file lock
1152 * @note do not try to lock/unlock the same offset in the same file from
1153 * different threads; use VGetDiskLock to protect threads from each other in
1154 * addition to other processes
1157 VLockFileLock(struct VLockFile
*lf
, afs_uint32 offset
, int locktype
, int nonblock
)
1161 osi_Assert(locktype
== READ_LOCK
|| locktype
== WRITE_LOCK
);
1163 MUTEX_ENTER(&lf
->mutex
);
1165 if (lf
->fd
== INVALID_FD
) {
1166 lf
->fd
= _VOpenPath(lf
->path
);
1167 if (lf
->fd
== INVALID_FD
) {
1168 MUTEX_EXIT(&lf
->mutex
);
1175 MUTEX_EXIT(&lf
->mutex
);
1177 code
= _VLockFd(lf
->fd
, offset
, locktype
, nonblock
);
1180 MUTEX_ENTER(&lf
->mutex
);
1181 if (--lf
->refcount
< 1) {
1183 lf
->fd
= INVALID_FD
;
1185 MUTEX_EXIT(&lf
->mutex
);
1192 VLockFileUnlock(struct VLockFile
*lf
, afs_uint32 offset
)
1194 MUTEX_ENTER(&lf
->mutex
);
1196 osi_Assert(lf
->fd
!= INVALID_FD
);
1198 if (--lf
->refcount
< 1) {
1200 lf
->fd
= INVALID_FD
;
1202 _VUnlockFd(lf
->fd
, offset
);
1205 MUTEX_EXIT(&lf
->mutex
);
1208 #ifdef AFS_DEMAND_ATTACH_FS
1211 * initialize a struct VDiskLock.
1213 * @param[in] dl struct VDiskLock to initialize
1214 * @param[in] lf the struct VLockFile to associate with this disk lock
1217 VDiskLockInit(struct VDiskLock
*dl
, struct VLockFile
*lf
, afs_uint32 offset
)
1220 memset(dl
, 0, sizeof(*dl
));
1221 Lock_Init(&dl
->rwlock
);
1222 MUTEX_INIT(&dl
->mutex
, "disklock", MUTEX_DEFAULT
, 0);
1223 CV_INIT(&dl
->cv
, "disklock cv", CV_DEFAULT
, 0);
1225 dl
->offset
= offset
;
1229 * acquire a lock on a file on local disk.
1231 * @param[in] dl the VDiskLock structure corresponding to the file on disk
1232 * @param[in] locktype READ_LOCK if you want a read lock, or WRITE_LOCK if
1233 * you want a write lock
1234 * @param[in] nonblock 0 to wait for conflicting locks to clear before
1235 * obtaining the lock; 1 to fail immediately if a
1236 * conflicting lock is held by someone else
1238 * @return operation status
1240 * @retval EBUSY someone else is holding a conflicting lock and nonblock=1 was
1242 * @retval EIO error acquiring file lock
1246 * @note while normal fcntl-y locks on Unix systems generally only work per-
1247 * process, this interface also deals with locks between threads in the
1248 * process in addition to different processes acquiring the lock
1251 VGetDiskLock(struct VDiskLock
*dl
, int locktype
, int nonblock
)
1254 osi_Assert(locktype
== READ_LOCK
|| locktype
== WRITE_LOCK
);
1257 if (locktype
== READ_LOCK
) {
1258 ObtainReadLockNoBlock(&dl
->rwlock
, code
);
1260 ObtainWriteLockNoBlock(&dl
->rwlock
, code
);
1267 } else if (locktype
== READ_LOCK
) {
1268 ObtainReadLock(&dl
->rwlock
);
1270 ObtainWriteLock(&dl
->rwlock
);
1273 MUTEX_ENTER(&dl
->mutex
);
1275 if ((dl
->flags
& VDISKLOCK_ACQUIRING
)) {
1276 /* Some other thread is waiting to acquire an fs lock. If nonblock=1,
1277 * we can return immediately, since we know we'll need to wait to
1278 * acquire. Otherwise, wait for the other thread to finish acquiring
1283 while ((dl
->flags
& VDISKLOCK_ACQUIRING
)) {
1284 CV_WAIT(&dl
->cv
, &dl
->mutex
);
1289 if (code
== 0 && !(dl
->flags
& VDISKLOCK_ACQUIRED
)) {
1290 /* no other thread holds the lock on the actual file; so grab one */
1292 /* first try, don't block on the lock to see if we can get it without
1294 code
= VLockFileLock(dl
->lockfile
, dl
->offset
, locktype
, 1);
1296 if (code
== EBUSY
&& !nonblock
) {
1298 /* mark that we are waiting on the fs lock */
1299 dl
->flags
|= VDISKLOCK_ACQUIRING
;
1301 MUTEX_EXIT(&dl
->mutex
);
1302 code
= VLockFileLock(dl
->lockfile
, dl
->offset
, locktype
, nonblock
);
1303 MUTEX_ENTER(&dl
->mutex
);
1305 dl
->flags
&= ~VDISKLOCK_ACQUIRING
;
1308 dl
->flags
|= VDISKLOCK_ACQUIRED
;
1311 CV_BROADCAST(&dl
->cv
);
1316 if (locktype
== READ_LOCK
) {
1317 ReleaseReadLock(&dl
->rwlock
);
1319 ReleaseWriteLock(&dl
->rwlock
);
1322 /* successfully got the lock, so inc the number of unlocks we need
1323 * to do before we can unlock the actual file */
1327 MUTEX_EXIT(&dl
->mutex
);
1333 * release a lock on a file on local disk.
1335 * @param[in] dl the struct VDiskLock to release
1336 * @param[in] locktype READ_LOCK if you are unlocking a read lock, or
1337 * WRITE_LOCK if you are unlocking a write lock
1339 * @return operation status
1343 VReleaseDiskLock(struct VDiskLock
*dl
, int locktype
)
1345 osi_Assert(locktype
== READ_LOCK
|| locktype
== WRITE_LOCK
);
1347 MUTEX_ENTER(&dl
->mutex
);
1348 osi_Assert(dl
->lockers
> 0);
1350 if (--dl
->lockers
< 1) {
1351 /* no threads are holding this lock anymore, so we can release the
1352 * actual disk lock */
1353 VLockFileUnlock(dl
->lockfile
, dl
->offset
);
1354 dl
->flags
&= ~VDISKLOCK_ACQUIRED
;
1357 MUTEX_EXIT(&dl
->mutex
);
1359 if (locktype
== READ_LOCK
) {
1360 ReleaseReadLock(&dl
->rwlock
);
1362 ReleaseWriteLock(&dl
->rwlock
);
1366 #endif /* AFS_DEMAND_ATTACH_FS */