Update NEWS for 1.6.22
[pkg-k5-afs_openafs.git] / src / vol / vutil.c
blob0bb10e340bc3fca49c12fe6f7dfa2ab9436083c5
1 /*
2 * Copyright 2000, International Business Machines Corporation and others.
3 * All Rights Reserved.
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 */
11 System: VICE-TWO
12 Module: vutil.c
13 Institution: The Information Technology Center, Carnegie-Mellon University
17 #include <afsconfig.h>
18 #include <afs/param.h>
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 #include <afs/afs_assert.h>
36 #include <rx/xdr.h>
37 #include <afs/afsint.h>
38 #include "nfs.h"
39 #include <afs/errors.h>
40 #include "lock.h"
41 #include "lwp.h"
42 #include <afs/afssyscalls.h>
43 #include "ihandle.h"
44 #include <afs/afsutil.h>
45 #ifdef AFS_NT40_ENV
46 #include "ntops.h"
47 #include <io.h>
48 #endif
49 #include "vnode.h"
50 #include "volume.h"
51 #include "volume_inline.h"
52 #include "partition.h"
53 #include "viceinode.h"
55 #include "volinodes.h"
56 #include "vol_prototypes.h"
57 #include "common.h"
59 #ifdef AFS_AIX_ENV
60 #include <sys/lockf.h>
61 #endif
62 #if defined(AFS_SUN5_ENV) || defined(AFS_NT40_ENV) || defined(AFS_LINUX20_ENV)
63 #include <string.h>
64 #else
65 #include <strings.h>
66 #endif
68 #ifdef O_LARGEFILE
69 #define afs_open open64
70 #else /* !O_LARGEFILE */
71 #define afs_open open
72 #endif /* !O_LARGEFILE */
75 #ifndef AFS_NT40_ENV
76 # ifdef O_LARGEFILE
77 # define AFS_SETLKW F_SETLKW64
78 # define AFS_SETLK F_SETLK64
79 # define afs_st_flock flock64
80 # else
81 # define AFS_SETLKW F_SETLKW
82 # define AFS_SETLK F_SETLK
83 # define afs_st_flock flock
84 # endif
85 #endif
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
92 static void
93 RemoveInodes(struct afs_inode_info *stuff, Device dev, VolumeId parent,
94 VolumeId vid)
96 int i;
97 IHandle_t *handle;
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);
108 } else {
109 IH_DEC(handle, inode, vid);
113 IH_RELEASE(handle);
116 Volume *
117 VCreateVolume(Error * ec, char *partname, VolId volumeId, VolId parentId)
118 { /* Should be the same as volumeId if there is
119 * no parent */
120 Volume *retVal;
121 VOL_LOCK;
122 retVal = VCreateVolume_r(ec, partname, volumeId, parentId);
123 VOL_UNLOCK;
124 return retVal;
127 Volume *
128 VCreateVolume_r(Error * ec, char *partname, VolId volumeId, VolId parentId)
129 { /* Should be the same as volumeId if there is
130 * no parent */
131 VolumeDiskData vol;
132 int i, rc;
133 char headerName[VMAXPATHLEN], volumePath[VMAXPATHLEN];
134 Device device;
135 struct DiskPartition64 *partition;
136 struct VolumeDiskHeader diskHeader;
137 IHandle_t *handle;
138 FdHandle_t *fdP;
139 Inode nearInode AFS_UNUSED = 0;
140 char *part, *name;
141 struct stat st;
142 afs_ino_str_t stmp;
143 struct VolumeHeader tempHeader;
144 struct afs_inode_info stuff[MAXINODETYPE];
145 # ifdef AFS_DEMAND_ATTACH_FS
146 int locktype = 0;
147 # endif /* AFS_DEMAND_ATTACH_FS */
149 init_inode_info(&tempHeader, stuff);
151 *ec = 0;
152 memset(&vol, 0, sizeof(vol));
153 vol.id = volumeId;
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. */
160 handle = NULL;
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);
165 *ec = VNOVOL;
166 return NULL;
168 #if defined(NEARINODE_HINT)
169 nearInodeHash(volumeId, nearInode);
170 nearInode %= partition->f_files;
171 #endif
172 VGetVolumePath(ec, vol.id, &part, &name);
173 if (*ec == VNOVOL || !strcmp(partition->name, part)) {
174 /* this case is ok */
175 } else {
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);
183 *ec = EXDEV;
184 return NULL;
187 *ec = 0;
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);
195 if (rc) {
196 Log("VCreateVolume: vol %lu already locked by someone else\n",
197 afs_printable_uint32_lu(volumeId));
198 *ec = VNOVOL;
199 return NULL;
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) {
218 if (rc == 0) {
219 Log("VCreateVolume: Header file %s already exists!\n",
220 volumePath);
221 *ec = VVOLEXISTS;
222 } else {
223 Log("VCreateVolume: Error %d trying to stat header file %s\n",
224 errno, volumePath);
225 *ec = VNOVOL;
227 goto bad_noheader;
229 device = partition->device;
231 for (i = 0; i < MAXINODETYPE; i++) {
232 struct afs_inode_info *p = &stuff[i];
233 if (p->obsolete)
234 continue;
235 #ifdef AFS_NAMEI_ENV
236 *(p->inode) =
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. */
243 IHandle_t *lh;
244 int code;
246 *(p->inode) = namei_MakeSpecIno(vol.parentId, VI_LINKTABLE);
247 IH_INIT(lh, device, parentId, *(p->inode));
248 fdP = IH_OPEN(lh);
249 if (fdP == NULL) {
250 IH_RELEASE(lh);
251 goto bad;
253 code = IH_INC(lh, *(p->inode), parentId);
254 FDH_REALLYCLOSE(fdP);
255 IH_RELEASE(lh);
256 if (code < 0)
257 goto bad;
258 continue;
261 #else
262 *(p->inode) =
263 IH_CREATE(NULL, device, VPartitionPath(partition), nearInode,
264 vol.id, INODESPECIAL, p->inodeType, vol.parentId);
265 #endif
267 if (!VALID_INO(*(p->inode))) {
268 Log("VCreateVolume: Problem creating %s file associated with volume header %s\n", p->description, volumePath);
269 bad:
270 if (handle)
271 IH_RELEASE(handle);
272 RemoveInodes(stuff, device, vol.parentId, vol.id);
273 if (!*ec) {
274 *ec = VNOVOL;
276 VDestroyVolumeDiskHeader(partition, volumeId, parentId);
277 bad_noheader:
278 # ifdef AFS_DEMAND_ATTACH_FS
279 if (locktype) {
280 VUnlockVolumeById(volumeId, partition);
282 # endif /* AFS_DEMAND_ATTACH_FS */
283 return NULL;
285 IH_INIT(handle, device, vol.parentId, *(p->inode));
286 fdP = IH_OPEN(handle);
287 if (fdP == NULL) {
288 Log("VCreateVolume: Problem iopen inode %s (err=%d)\n",
289 PrintInode(stmp, *(p->inode)), errno);
290 goto bad;
292 if (FDH_PWRITE(fdP, (char *)&p->stamp, sizeof(p->stamp), 0) !=
293 sizeof(p->stamp)) {
294 Log("VCreateVolume: Problem writing to inode %s (err=%d)\n",
295 PrintInode(stmp, *(p->inode)), errno);
296 FDH_REALLYCLOSE(fdP);
297 goto bad;
299 FDH_REALLYCLOSE(fdP);
300 IH_RELEASE(handle);
301 nearInode = *(p->inode);
304 IH_INIT(handle, device, vol.parentId, tempHeader.volumeInfo);
305 fdP = IH_OPEN(handle);
306 if (fdP == NULL) {
307 Log("VCreateVolume: Problem iopen inode %s (err=%d)\n",
308 PrintInode(stmp, tempHeader.volumeInfo), errno);
309 goto bad;
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);
315 goto bad;
317 FDH_CLOSE(fdP);
318 IH_RELEASE(handle);
320 VolumeHeaderToDisk(&diskHeader, &tempHeader);
321 rc = VCreateVolumeDiskHeader(&diskHeader, partition);
322 if (rc) {
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));
326 if (rc == EEXIST) {
327 *ec = VVOLEXISTS;
329 goto bad;
332 # ifdef AFS_DEMAND_ATTACH_FS
333 if (locktype) {
334 VUnlockVolumeById(volumeId, partition);
336 # endif /* AFS_DEMAND_ATTACH_FS */
337 return (VAttachVolumeByName_r(ec, partname, headerName, V_SECRETLY));
339 #endif /* FSSYNC_BUILD_CLIENT */
342 void
343 AssignVolumeName(VolumeDiskData * vol, char *name, char *ext)
345 VOL_LOCK;
346 AssignVolumeName_r(vol, name, ext);
347 VOL_UNLOCK;
350 void
351 AssignVolumeName_r(VolumeDiskData * vol, char *name, char *ext)
353 char *dot;
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))
358 *dot = 0;
359 if (ext)
360 strncat(vol->name, ext, VNAMESIZE - 1 - strlen(vol->name));
363 afs_int32
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). */
373 Date copydate;
374 VolumeId id, parent;
375 id = to->id;
376 parent = to->parentId;
377 copydate = to->copyDate;
378 memcpy(to, from, sizeof(*from));
379 to->id = id;
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;
385 return 0;
388 afs_int32
389 CopyVolumeHeader(VolumeDiskData * from, VolumeDiskData * to)
391 afs_int32 code;
393 VOL_LOCK;
394 code = CopyVolumeHeader_r(from, to);
395 VOL_UNLOCK;
396 return (code);
399 void
400 ClearVolumeStats(VolumeDiskData * vol)
402 VOL_LOCK;
403 ClearVolumeStats_r(vol);
404 VOL_UNLOCK;
407 void
408 ClearVolumeStats_r(VolumeDiskData * vol)
410 memset(vol->weekUse, 0, sizeof(vol->weekUse));
411 vol->dayUse = 0;
412 vol->dayUseDate = 0;
415 void
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));
435 void
436 CopyVolumeStats(VolumeDiskData * from, VolumeDiskData * to)
438 VOL_LOCK;
439 CopyVolumeStats_r(from, to);
440 VOL_UNLOCK;
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
451 * header
453 * @return operation status
454 * @retval 0 success
455 * @retval -1 volume header doesn't exist
456 * @retval EIO failed to read volume header
458 * @internal
460 afs_int32
461 VReadVolumeDiskHeader(VolumeId volid,
462 struct DiskPartition64 * dp,
463 VolumeDiskHeader_t * hdr)
465 afs_int32 code = 0;
466 int fd;
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);
473 if (fd < 0) {
474 Log("VReadVolumeDiskHeader: Couldn't open header for volume %lu (errno %d).\n",
475 afs_printable_uint32_lu(volid), errno);
476 code = -1;
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));
481 code = EIO;
484 if (fd >= 0) {
485 close(fd);
487 return code;
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
499 * @retval 0 success
500 * @retval -1 volume header doesn't exist
501 * @retval EIO failed to write volume header
503 * @internal
505 static afs_int32
506 _VWriteVolumeDiskHeader(VolumeDiskHeader_t * hdr,
507 struct DiskPartition64 * dp,
508 int flags)
510 afs_int32 code = 0;
511 int fd;
512 char path[MAXPATHLEN];
514 #ifdef AFS_DEMAND_ATTACH_FS
515 /* prevent racing with VGC scanners reading the vol header while we are
516 * writing it */
517 code = VPartHeaderLock(dp, READ_LOCK);
518 if (code) {
519 return EIO;
521 #endif /* AFS_DEMAND_ATTACH_FS */
523 flags |= O_RDWR;
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);
529 if (fd < 0) {
530 code = errno;
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);
536 code = EIO;
539 if (fd >= 0) {
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 */
550 return code;
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
560 * @retval 0 success
561 * @retval ENOENT volume header doesn't exist
562 * @retval EIO failed to write volume header
564 afs_int32
565 VWriteVolumeDiskHeader(VolumeDiskHeader_t * hdr,
566 struct DiskPartition64 * dp)
568 afs_int32 code;
570 #ifdef AFS_DEMAND_ATTACH_FS
571 VolumeDiskHeader_t oldhdr;
572 int delvgc = 0, addvgc = 0;
573 SYNC_response res;
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 */
582 delvgc = 1;
583 addvgc = 1;
585 } else if (code) {
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 */
588 addvgc = 1;
591 #endif /* AFS_DEMAND_ATTACH_FS */
593 code = _VWriteVolumeDiskHeader(hdr, dp, 0);
594 if (code) {
595 goto done;
598 #ifdef AFS_DEMAND_ATTACH_FS
599 if (delvgc) {
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));
615 if (addvgc) {
616 memset(&res, 0, sizeof(res));
617 code = FSYNC_VGCAdd(dp->name, hdr->parent, hdr->id, FSYNC_WHATEVER, &res);
618 if (code) {
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 */
630 done:
631 return code;
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
641 * @retval 0 success
642 * @retval EEXIST volume header already exists
643 * @retval EIO failed to write volume header
645 * @internal
647 afs_int32
648 VCreateVolumeDiskHeader(VolumeDiskHeader_t * hdr,
649 struct DiskPartition64 * dp)
651 afs_int32 code = 0;
652 #ifdef AFS_DEMAND_ATTACH_FS
653 SYNC_response res;
654 #endif /* AFS_DEMAND_ATTACH_FS */
656 code = _VWriteVolumeDiskHeader(hdr, dp, O_CREAT | O_EXCL);
657 if (code) {
658 goto done;
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);
664 if (code) {
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 */
674 done:
675 return code;
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
687 * @retval 0 success
689 * @note if parent is 0, the parent volume ID will be looked up from the
690 * fileserver
692 * @note for non-DAFS, parent is currently ignored
694 afs_int32
695 VDestroyVolumeDiskHeader(struct DiskPartition64 * dp,
696 VolumeId volid,
697 VolumeId parent)
699 afs_int32 code = 0;
700 char path[MAXPATHLEN];
701 #ifdef AFS_DEMAND_ATTACH_FS
702 SYNC_response res;
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));
708 code = unlink(path);
709 if (code) {
710 Log("VDestroyVolumeDiskHeader: Couldn't unlink disk header, error = %d\n", errno);
711 goto done;
714 #ifdef AFS_DEMAND_ATTACH_FS
715 memset(&res, 0, sizeof(res));
716 if (!parent) {
717 FSSYNC_VGQry_response_t q_res;
719 code = FSYNC_VGCQuery(dp->name, volid, &q_res, &res);
720 if (code) {
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));
725 goto done;
728 parent = q_res.rw;
731 code = FSYNC_VGCDel(dp->name, parent, volid, FSYNC_WHATEVER, &res);
732 if (code) {
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 */
742 done:
743 return code;
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
758 * @retval 0 success
759 * @retval -1 fatal error, stop scanning
760 * @retval 1 failed to read header
761 * @retval 2 volfunc callback indicated error after header read
763 static int
764 _VHandleVolumeHeader(struct DiskPartition64 *dp, VWalkVolFunc volfunc,
765 const char *name, struct VolumeDiskHeader *hdr,
766 int locked, void *rock)
768 int error = 0;
769 int fd;
771 if ((fd = afs_open(name, O_RDONLY)) == -1
772 || read(fd, hdr, sizeof(*hdr))
773 != sizeof(*hdr)
774 || hdr->stamp.magic != VOLUMEHEADERMAGIC) {
775 error = 1;
778 if (fd >= 0) {
779 close(fd);
782 #ifdef AFSFS_DEMAND_ATTACH_FS
783 if (locked) {
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 */
791 int last = 1;
793 #ifdef AFS_DEMAND_ATTACH_FS
794 if (!locked) {
795 last = 0;
797 #endif /* AFS_DEMAND_ATTACH_FS */
799 error = (*volfunc) (dp, name, hdr, last, rock);
800 if (error < 0) {
801 return -1;
803 if (error) {
804 error = 2;
808 #ifdef AFS_DEMAND_ATTACH_FS
809 if (error && !locked) {
810 int code;
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);
815 if (code) {
816 Log("Error acquiring partition write lock when "
817 "looking at header %s\n", name);
818 return -1;
821 return _VHandleVolumeHeader(dp, volfunc, name, hdr, 1, rock);
823 #endif /* AFS_DEMAND_ATTACH_FS */
825 return error;
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
848 * @see VWalkVolFunc
849 * @see VWalkErrFunc
851 * @return operation status
852 * @retval 0 success
853 * @retval negative fatal error, walk did not finish
856 VWalkVolumeHeaders(struct DiskPartition64 *dp, const char *partpath,
857 VWalkVolFunc volfunc, VWalkErrFunc errfunc, void *rock)
859 DIR *dirp = NULL;
860 struct dirent *dentry = NULL;
861 int code = 0;
862 struct VolumeDiskHeader diskHeader;
864 dirp = opendir(partpath);
865 if (!dirp) {
866 Log("VWalkVolumeHeaders: cannot open directory %s\n", partpath);
867 code = -1;
868 goto done;
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);
880 if (code < 0) {
881 /* fatal error, stop walking */
882 goto done;
884 if (code && errfunc) {
885 /* error with header; call the caller-supplied vol error
886 * function */
888 struct VolumeDiskHeader *hdr = &diskHeader;
889 if (code == 1) {
890 /* we failed to read the header at all, so don't pass in
891 * the header ptr */
892 hdr = NULL;
894 (*errfunc) (dp, name, hdr, rock);
896 code = 0;
899 done:
900 if (dirp) {
901 closedir(dirp);
902 dirp = NULL;
905 return code;
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
913 * are copied.
915 void
916 VLockFileInit(struct VLockFile *lf, const char *path)
918 memset(lf, 0, sizeof(*lf));
919 lf->path = strdup(path);
920 lf->fd = INVALID_FD;
921 MUTEX_INIT(&lf->mutex, "vlockfile", MUTEX_DEFAULT, 0);
924 #ifdef AFS_NT40_ENV
925 static_inline FD_t
926 _VOpenPath(const char *path)
928 HANDLE handle;
930 handle = CreateFile(path,
931 GENERIC_READ | GENERIC_WRITE,
932 FILE_SHARE_READ | FILE_SHARE_WRITE,
933 NULL,
934 OPEN_ALWAYS,
935 FILE_ATTRIBUTE_HIDDEN,
936 NULL);
937 if (handle == INVALID_HANDLE_VALUE) {
938 return INVALID_FD;
941 return handle;
944 static_inline int
945 _VLockFd(FD_t handle, afs_uint32 offset, int locktype, int nonblock)
947 DWORD flags = 0;
948 OVERLAPPED lap;
950 if (locktype == WRITE_LOCK) {
951 flags |= LOCKFILE_EXCLUSIVE_LOCK;
953 if (nonblock) {
954 flags |= LOCKFILE_FAIL_IMMEDIATELY;
957 memset(&lap, 0, sizeof(lap));
958 lap.Offset = offset;
960 if (!LockFileEx(handle, flags, 0, 1, 0, &lap)) {
961 if (GetLastError() == ERROR_LOCK_VIOLATION) {
962 return EBUSY;
964 return EIO;
967 return 0;
970 static_inline void
971 _VUnlockFd(struct VLockFile *lf, afs_uint32 offset)
973 OVERLAPPED lap;
975 memset(&lap, 0, sizeof(lap));
976 lap.Offset = offset;
978 UnlockFileEx(lf->fd, 0, 1, 0, &lap);
981 static_inline void
982 _VCloseFd(struct VLockFile *lf)
984 CloseHandle(lf->fd);
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
997 static_inline int
998 _VOpenPath(const char *path)
1000 int fd;
1002 fd = open(path, O_RDWR | O_CREAT, 0660);
1003 if (fd < 0) {
1004 return INVALID_FD;
1006 return fd;
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
1018 * @retval 0 success
1019 * @retval EBUSY someone else is holding a conflicting lock and nonblock=1 was
1020 * specified
1021 * @retval EIO error acquiring file lock
1023 static_inline int
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) {
1031 l_type = F_RDLCK;
1033 if (nonblock) {
1034 cmd = AFS_SETLK;
1037 sf.l_start = offset;
1038 sf.l_len = 1;
1039 sf.l_type = l_type;
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 */
1045 sf.l_pid = 0;
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));
1051 } else {
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,
1055 locktype);
1057 return EBUSY;
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));
1062 return EIO;
1065 return 0;
1069 * close a file descriptor used for file locking.
1071 * @param[in] fd file descriptor to close
1073 static_inline void
1074 _VCloseFd(int fd)
1076 if (close(fd)) {
1077 Log("_VCloseFd: error %d closing fd %d\n",
1078 errno, fd);
1083 * unlock a file offset in a file descriptor.
1085 * @param[in] fd file descriptor to unlock
1086 * @param[in] offset offset to unlock
1088 static_inline void
1089 _VUnlockFd(int fd, afs_uint32 offset)
1091 struct afs_st_flock sf;
1093 sf.l_start = offset;
1094 sf.l_len = 1;
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
1112 * this.
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
1119 void
1120 VLockFileReinit(struct VLockFile *lf)
1122 MUTEX_ENTER(&lf->mutex);
1124 if (lf->fd != INVALID_FD) {
1125 _VCloseFd(lf->fd);
1126 lf->fd = INVALID_FD;
1129 lf->refcount = 0;
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
1145 * @retval 0 success
1146 * @retval EBUSY someone else is holding a conflicting lock and nonblock=1 was
1147 * specified
1148 * @retval EIO error acquiring file lock
1150 * @note DAFS only
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)
1159 int code;
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);
1169 return EIO;
1173 lf->refcount++;
1175 MUTEX_EXIT(&lf->mutex);
1177 code = _VLockFd(lf->fd, offset, locktype, nonblock);
1179 if (code) {
1180 MUTEX_ENTER(&lf->mutex);
1181 if (--lf->refcount < 1) {
1182 _VCloseFd(lf->fd);
1183 lf->fd = INVALID_FD;
1185 MUTEX_EXIT(&lf->mutex);
1188 return code;
1191 void
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) {
1199 _VCloseFd(lf->fd);
1200 lf->fd = INVALID_FD;
1201 } else {
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
1216 void
1217 VDiskLockInit(struct VDiskLock *dl, struct VLockFile *lf, afs_uint32 offset)
1219 osi_Assert(lf);
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);
1224 dl->lockfile = lf;
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
1239 * @retval 0 success
1240 * @retval EBUSY someone else is holding a conflicting lock and nonblock=1 was
1241 * specified
1242 * @retval EIO error acquiring file lock
1244 * @note DAFS only
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)
1253 int code = 0;
1254 osi_Assert(locktype == READ_LOCK || locktype == WRITE_LOCK);
1256 if (nonblock) {
1257 if (locktype == READ_LOCK) {
1258 ObtainReadLockNoBlock(&dl->rwlock, code);
1259 } else {
1260 ObtainWriteLockNoBlock(&dl->rwlock, code);
1263 if (code) {
1264 return EBUSY;
1267 } else if (locktype == READ_LOCK) {
1268 ObtainReadLock(&dl->rwlock);
1269 } else {
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
1279 * the fs lock */
1280 if (nonblock) {
1281 code = EBUSY;
1282 } else {
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
1293 * waiting */
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;
1307 if (code == 0) {
1308 dl->flags |= VDISKLOCK_ACQUIRED;
1311 CV_BROADCAST(&dl->cv);
1315 if (code) {
1316 if (locktype == READ_LOCK) {
1317 ReleaseReadLock(&dl->rwlock);
1318 } else {
1319 ReleaseWriteLock(&dl->rwlock);
1321 } else {
1322 /* successfully got the lock, so inc the number of unlocks we need
1323 * to do before we can unlock the actual file */
1324 ++dl->lockers;
1327 MUTEX_EXIT(&dl->mutex);
1329 return code;
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
1340 * @retval 0 success
1342 void
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);
1361 } else {
1362 ReleaseWriteLock(&dl->rwlock);
1366 #endif /* AFS_DEMAND_ATTACH_FS */