rename afs/assert.h to afs/afs_assert.h
[openafs.git] / src / vol / partition.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  * Portions Copyright (c) 2003 Apple Computer, Inc.
10  * Portions Copyright (c) 2006 Sine Nomine Associates
11  */
12
13 /*
14
15         System:         VICE-TWO
16         Module:         partition.c
17         Institution:    The Information Technology Center, Carnegie-Mellon University
18
19  */
20
21 #include <afsconfig.h>
22 #include <afs/param.h>
23
24
25 #include <ctype.h>
26 #include <string.h>
27 #ifdef AFS_NT40_ENV
28 #include <windows.h>
29 #include <winbase.h>
30 #include <winioctl.h>
31 #else
32 #include <sys/param.h>
33 #include <sys/types.h>
34 #include <unistd.h>
35
36 #if AFS_HAVE_STATVFS || AFS_HAVE_STATVFS64
37 #include <sys/statvfs.h>
38 #endif /* AFS_HAVE_STATVFS */
39 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
40 #include <sys/mount.h>
41 #endif
42
43 #if !defined(AFS_SGI_ENV)
44 #ifdef  AFS_OSF_ENV
45 #include <sys/mount.h>
46 #include <ufs/fs.h>
47 #else /* AFS_OSF_ENV */
48 #ifdef AFS_VFSINCL_ENV
49 #define VFS
50 #ifdef  AFS_SUN5_ENV
51 #include <sys/fs/ufs_fs.h>
52 #else
53 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
54 #include <ufs/ufs/dinode.h>
55 #include <ufs/ffs/fs.h>
56 #else
57 #include <ufs/fs.h>
58 #endif
59 #endif
60 #else /* AFS_VFSINCL_ENV */
61 #if !defined(AFS_AIX_ENV) && !defined(AFS_LINUX22_ENV) && !defined(AFS_DARWIN_ENV) && !defined(AFS_XBSD_ENV)
62 #include <sys/fs.h>
63 #endif
64 #endif /* AFS_VFSINCL_ENV */
65 #endif /* AFS_OSF_ENV */
66 #include <errno.h>
67 #include <sys/stat.h>
68 #include <stdio.h>
69 #include <sys/file.h>
70 #ifdef  AFS_AIX_ENV
71 #include <sys/vfs.h>
72 #include <sys/lockf.h>
73 #else
74 #ifdef  AFS_HPUX_ENV
75 #include <sys/vfs.h>
76 #include <unistd.h>
77 #include <fcntl.h>
78 #include <checklist.h>
79 #else
80 #if     defined(AFS_SUN_ENV)
81 #include <sys/vfs.h>
82 #ifndef AFS_SUN5_ENV
83 #include <mntent.h>
84 #endif
85 #endif
86 #ifdef AFS_SUN5_ENV
87 #include <unistd.h>
88 #include <sys/mnttab.h>
89 #include <sys/mntent.h>
90 #else
91 #ifdef AFS_LINUX22_ENV
92 #include <mntent.h>
93 #include <sys/statfs.h>
94 #else
95 #include <fstab.h>
96 #endif
97 #endif
98 #endif
99 #endif
100 #endif /* AFS_SGI_ENV */
101 #endif /* AFS_NT40_ENV */
102 #if defined(AFS_SGI_ENV)
103 #include <sys/errno.h>
104 #include <sys/stat.h>
105 #include <stdio.h>
106 #include <sys/file.h>
107 #include <mntent.h>
108 #endif
109
110 #include <rx/xdr.h>
111 #include <afs/afsint.h>
112 #include "nfs.h"
113 #include <afs/errors.h>
114 #include "lock.h"
115 #include "lwp.h"
116 #include <afs/afssyscalls.h>
117 #include "ihandle.h"
118 #include "common.h"
119 #ifdef AFS_NAMEI_ENV
120 #ifdef AFS_NT40_ENV
121 #include "ntops.h"
122 #else
123 #include "namei_ops.h"
124 #include <dirent.h>
125 #endif /* AFS_NT40_ENV */
126 #endif /* AFS_NAMEI_ENV */
127 #include "vnode.h"
128 #include "volume.h"
129 #include "partition.h"
130 #ifdef AFS_PTHREAD_ENV
131 #include <assert.h>
132 #else /* AFS_PTHREAD_ENV */
133 #include <afs/afs_assert.h>
134 #endif /* AFS_PTHREAD_ENV */
135
136 #if defined(AFS_HPUX_ENV)
137 #include <sys/types.h>
138 #include <sys/privgrp.h>
139 #endif /* defined(AFS_HPUX_ENV) */
140
141 #ifdef AFS_AIX42_ENV
142 #include <jfs/filsys.h>
143 #endif
144
145 #ifdef O_LARGEFILE
146
147 #define afs_stat        stat64
148 #define afs_open        open64
149 #define afs_fopen       fopen64
150 #ifndef AFS_NT40_ENV
151 #if AFS_HAVE_STATVFS64
152 # define afs_statvfs    statvfs64
153 #else
154 # if AFS_HAVE_STATFS64
155 #  define afs_statfs    statfs64
156 #else
157 #  if AFS_HAVE_STATVFS
158 #   define afs_statvfs  statvfs
159 #  else
160 #   define afs_statfs   statfs
161 #  endif /* !AFS_HAVE_STATVFS */
162 # endif /* !AFS_HAVE_STATFS64 */
163 #endif /* !AFS_HAVE_STATVFS64 */
164 #endif /* !AFS_NT40_ENV */
165
166 #else /* !O_LARGEFILE */
167
168 #define afs_stat        stat
169 #define afs_open        open
170 #define afs_fopen       fopen
171 #ifndef AFS_NT40_ENV
172 #if AFS_HAVE_STATVFS
173 #define afs_statvfs     statvfs
174 #else /* !AFS_HAVE_STATVFS */
175 #define afs_statfs      statfs
176 #endif /* !AFS_HAVE_STATVFS */
177 #endif /* !AFS_NT40_ENV */
178
179 #endif /* !O_LARGEFILE */
180
181 int aixlow_water = 8;           /* default 8% */
182 struct DiskPartition64 *DiskPartitionList;
183
184 #ifdef AFS_DEMAND_ATTACH_FS
185 /* file to lock to conceptually "lock" the vol headers on a partition */
186 #define AFS_PARTLOCK_FILE ".volheaders.lock"
187 #define AFS_VOLUMELOCK_FILE ".volume.lock"
188
189 static struct DiskPartition64 *DiskPartitionTable[VOLMAXPARTS+1];
190
191 static struct DiskPartition64 * VLookupPartition_r(char * path);
192 static void AddPartitionToTable_r(struct DiskPartition64 *);
193 #endif /* AFS_DEMAND_ATTACH_FS */
194
195 #ifdef AFS_SGI_XFS_IOPS_ENV
196 /* Verify that the on disk XFS inodes on the partition are large enough to
197  * hold the AFS attribute. Returns -1 if the attribute can't be set or is
198  * too small to fit in the inode. Returns 0 if the attribute does fit in
199  * the XFS inode.
200  */
201 #include <afs/xfsattrs.h>
202 static int
203 VerifyXFSInodeSize(char *part, char *fstype)
204 {
205     afs_xfs_attr_t junk;
206     int length = SIZEOF_XFS_ATTR_T;
207     int fd = 0;
208     int code = -1;
209     struct fsxattr fsx;
210
211     if (strcmp("xfs", fstype))
212         return 0;
213
214     if (attr_set(part, AFS_XFS_ATTR, &junk, length, ATTR_ROOT) == 0) {
215         if (((fd = open(part, O_RDONLY, 0)) != -1)
216             && (fcntl(fd, F_FSGETXATTRA, &fsx) == 0)) {
217
218             if (fsx.fsx_nextents) {
219                 Log("Partition %s: XFS inodes too small, exiting.\n", part);
220                 Log("Run xfs_size_check utility and remake partitions.\n");
221             } else
222                 code = 0;
223         }
224
225         if (fd > 0)
226             close(fd);
227         (void)attr_remove(part, AFS_XFS_ATTR, ATTR_ROOT);
228     }
229     return code;
230 }
231 #endif /* AFS_SGI_XFS_IOPS_ENV */
232
233 int
234 VInitPartitionPackage(void)
235 {
236 #ifdef AFS_DEMAND_ATTACH_ENV
237     memset(&DiskPartitionTable, 0, sizeof(DiskPartitionTable));
238 #endif /* AFS_DEMAND_ATTACH_ENV */
239     return 0;
240 }
241
242 static void
243 VInitPartition_r(char *path, char *devname, Device dev)
244 {
245     struct DiskPartition64 *dp, *op;
246
247     dp = (struct DiskPartition64 *)malloc(sizeof(struct DiskPartition64));
248     /* Add it to the end, to preserve order when we print statistics */
249     for (op = DiskPartitionList; op; op = op->next) {
250         if (!op->next)
251             break;
252     }
253     if (op)
254         op->next = dp;
255     else
256         DiskPartitionList = dp;
257     dp->next = 0;
258     dp->name = (char *)malloc(strlen(path) + 1);
259     strncpy(dp->name, path, strlen(path) + 1);
260     dp->index = volutil_GetPartitionID(path);
261 #if defined(AFS_NAMEI_ENV) && !defined(AFS_NT40_ENV)
262     /* Create a lockfile for the partition, of the form /vicepa/Lock/vicepa */
263     dp->devName = (char *)malloc(2 * strlen(path) + 6);
264     strcpy(dp->devName, path);
265     strcat(dp->devName, "/");
266     strcat(dp->devName, "Lock");
267     mkdir(dp->devName, 0700);
268     strcat(dp->devName, path);
269     close(afs_open(dp->devName, O_RDWR | O_CREAT, 0600));
270     dp->device = dp->index;
271 #else
272     dp->devName = (char *)malloc(strlen(devname) + 1);
273     strncpy(dp->devName, devname, strlen(devname) + 1);
274     dp->device = dev;
275 #endif
276     dp->lock_fd = INVALID_FD;
277     dp->flags = 0;
278     dp->f_files = 1;            /* just a default value */
279 #if defined(AFS_NAMEI_ENV) && !defined(AFS_NT40_ENV)
280     if (programType == fileServer)
281         (void)namei_ViceREADME(VPartitionPath(dp));
282 #endif
283     VSetPartitionDiskUsage_r(dp);
284 #ifdef AFS_DEMAND_ATTACH_FS
285     AddPartitionToTable_r(dp);
286     queue_Init(&dp->vol_list.head);
287     assert(pthread_cond_init(&dp->vol_list.cv, NULL) == 0);
288     dp->vol_list.len = 0;
289     dp->vol_list.busy = 0;
290     {
291         char lockpath[MAXPATHLEN+1];
292         afs_snprintf(lockpath, MAXPATHLEN, "%s/" AFS_PARTLOCK_FILE, dp->name);
293         lockpath[MAXPATHLEN] = '\0';
294         VLockFileInit(&dp->headerLockFile, lockpath);
295
296         afs_snprintf(lockpath, MAXPATHLEN, "%s/" AFS_VOLUMELOCK_FILE, dp->name);
297         lockpath[MAXPATHLEN] = '\0';
298         VLockFileInit(&dp->volLockFile, lockpath);
299     }
300     VDiskLockInit(&dp->headerLock, &dp->headerLockFile, 1);
301 #endif /* AFS_DEMAND_ATTACH_FS */
302 }
303
304 static void
305 VInitPartition(char *path, char *devname, Device dev)
306 {
307     VOL_LOCK;
308     VInitPartition_r(path, devname, dev);
309     VOL_UNLOCK;
310 }
311
312 #ifndef AFS_NT40_ENV
313 /* VAttachPartitions() finds the vice partitions on this server. Calls
314  * VCheckPartition() to do some basic checks on the partition. If the partition
315  * is a valid vice partition, VCheckPartition will add it to the DiskPartition
316  * list.
317  * Returns the number of errors returned by VCheckPartition. An error in
318  * VCheckPartition means that partition is a valid vice partition but the
319  * fileserver should not start because of the error found on that partition.
320  *
321  * AFS_NAMEI_ENV
322  * No specific user space file system checks, since we don't know what
323  * is being used for vice partitions.
324  *
325  * Use partition name as devname.
326  */
327 int
328 VCheckPartition(char *part, char *devname)
329 {
330     struct afs_stat status;
331 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_NT40_ENV)
332     char AFSIDatPath[MAXPATHLEN];
333 #endif
334
335     /* Only keep track of "/vicepx" partitions since it can get hairy
336      * when NFS mounts are involved.. */
337     if (strncmp(part, VICE_PARTITION_PREFIX, VICE_PREFIX_SIZE)) {
338         return 0;
339     }
340     if (afs_stat(part, &status) < 0) {
341         Log("VInitVnodes: Couldn't find file system %s; ignored\n", part);
342         return 0;
343     }
344 #ifndef AFS_AIX32_ENV
345     if (programType == fileServer) {
346         char salvpath[MAXPATHLEN];
347         strcpy(salvpath, part);
348         strcat(salvpath, "/FORCESALVAGE");
349         if (afs_stat(salvpath, &status) == 0) {
350             Log("VInitVnodes: Found %s; aborting\n", salvpath);
351             return -1;
352         }
353     }
354 #endif
355
356 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_NT40_ENV)
357     strcpy(AFSIDatPath, part);
358     strcat(AFSIDatPath, "/AFSIDat");
359 #ifdef AFS_NAMEI_ENV
360     if (afs_stat(AFSIDatPath, &status) < 0) {
361         DIR *dirp;
362         struct dirent *dp;
363
364         dirp = opendir(part);
365         assert(dirp);
366         while ((dp = readdir(dirp))) {
367             if (dp->d_name[0] == 'V') {
368                 Log("This program is compiled with AFS_NAMEI_ENV, but partition %s seems to contain volumes which don't use the namei-interface; aborting\n", part);
369                 closedir(dirp);
370                 return -1;
371             }
372         }
373         closedir(dirp);
374     }
375 #else /* AFS_NAMEI_ENV */
376     if (afs_stat(AFSIDatPath, &status) == 0) {
377         Log("This program is compiled without AFS_NAMEI_ENV, but partition %s seems to contain volumes which use the namei-interface; aborting\n", part);
378         return -1;
379     }
380
381 #ifdef AFS_SGI_XFS_IOPS_ENV
382     if (VerifyXFSInodeSize(part, status.st_fstype) < 0)
383         return -1;
384 #endif
385 #endif /* AFS_NAMEI_ENV */
386 #endif /* !AFS_LINUX20_ENV && !AFS_NT40_ENV */
387
388 #if defined(AFS_DUX40_ENV) && !defined(AFS_NAMEI_ENV)
389     if (status.st_ino != ROOTINO) {
390         Log("%s is not a mounted file system; ignored.\n", part);
391         return 0;
392     }
393 #endif
394
395     VInitPartition(part, devname, status.st_dev);
396
397     return 0;
398 }
399
400 /* VIsAlwaysAttach() checks whether a /vicepX directory should always be
401  * attached (return value 1), or only attached when it is a separately
402  * mounted partition (return value 0).  For non-NAMEI environments, it
403  * always returns 0.
404  *
405  * *awouldattach will be set to 1 if the given path at least looks like a vice
406  * partition (that is, if we return 0, the only thing preventing this partition
407  * from being attached is the existence of the AlwaysAttach file), or to 0
408  * otherwise. *awouldattach is set regardless of whether or not the partition
409  * should always be attached or not.
410  */
411 static int
412 VIsAlwaysAttach(char *part, int *awouldattach)
413 {
414 #ifdef AFS_NAMEI_ENV
415     struct afs_stat st;
416     char checkfile[256];
417     int ret;
418 #endif /* AFS_NAMEI_ENV */
419
420     if (awouldattach) {
421         *awouldattach = 0;
422     }
423
424 #ifdef AFS_NAMEI_ENV
425     if (strncmp(part, VICE_PARTITION_PREFIX, VICE_PREFIX_SIZE))
426         return 0;
427
428     if (awouldattach) {
429         *awouldattach = 1;
430     }
431
432     strncpy(checkfile, part, 100);
433     strcat(checkfile, "/");
434     strcat(checkfile, VICE_ALWAYSATTACH_FILE);
435
436     ret = afs_stat(checkfile, &st);
437     return (ret < 0) ? 0 : 1;
438 #else /* AFS_NAMEI_ENV */
439     return 0;
440 #endif /* AFS_NAMEI_ENV */
441 }
442
443 /* VAttachPartitions2() looks for and attaches /vicepX partitions
444  * where a special file (VICE_ALWAYSATTACH_FILE) exists.  This is
445  * used to attach /vicepX directories which aren't on dedicated
446  * partitions, in the NAMEI fileserver.
447  */
448 void
449 VAttachPartitions2(void)
450 {
451 #ifdef AFS_NAMEI_ENV
452     DIR *dirp;
453     struct dirent *de;
454     char pname[32];
455     int wouldattach;
456
457     dirp = opendir("/");
458     while ((de = readdir(dirp))) {
459         strcpy(pname, "/");
460         strncat(pname, de->d_name, 20);
461         pname[sizeof(pname) - 1] = '\0';
462
463         /* Only keep track of "/vicepx" partitions since automounter
464          * may hose us */
465         if (VIsAlwaysAttach(pname, &wouldattach)) {
466             VCheckPartition(pname, "");
467         } else {
468             struct afs_stat st;
469             if (wouldattach && VGetPartition(pname, 0) == NULL &&
470                 afs_stat(pname, &st) == 0 && S_ISDIR(st.st_mode)) {
471
472                 /* This is a /vicep* dir, and it has not been attached as a
473                  * partition. This probably means that this is a /vicep* dir
474                  * that is not a separate partition, so just give a notice so
475                  * admins are not confused as to why their /vicep* dirs are not
476                  * being attached.
477                  *
478                  * It is possible that the dir _is_ a separate partition and we
479                  * failed to attach it earlier, making this message a bit
480                  * confusing. But that should be rare, and an error message
481                  * about the failure will already be logged right before this,
482                  * so it should be clear enough. */
483
484                 Log("VAttachPartitions: not attaching %s; either it is not a "
485                     "separate partition, or it failed to attach (create the "
486                     "file %s/" VICE_ALWAYSATTACH_FILE " to force attachment)\n",
487                     pname, pname);
488             }
489         }
490     }
491     closedir(dirp);
492 #endif /* AFS_NAMEI_ENV */
493 }
494 #endif /* AFS_NT40_ENV */
495
496 #ifdef AFS_SUN5_ENV
497 int
498 VAttachPartitions(void)
499 {
500     int errors = 0;
501     struct mnttab mnt;
502     FILE *mntfile;
503
504     if (!(mntfile = afs_fopen(MNTTAB, "r"))) {
505         Log("Can't open %s\n", MNTTAB);
506         perror(MNTTAB);
507         exit(-1);
508     }
509     while (!getmntent(mntfile, &mnt)) {
510         /* Ignore non ufs or non read/write partitions */
511         /* but allow zfs too if we're in the NAMEI environment */
512         if (
513 #ifdef AFS_NAMEI_ENV
514             (((strcmp(mnt.mnt_fstype, "ufs") &&
515                 strcmp(mnt.mnt_fstype, "zfs"))))
516 #else
517             (strcmp(mnt.mnt_fstype, "ufs") != 0)
518 #endif
519             || (strncmp(mnt.mnt_mntopts, "ro,ignore", 9) == 0))
520             continue;
521
522         /* If we're going to always attach this partition, do it later. */
523         if (VIsAlwaysAttach(mnt.mnt_mountp, NULL))
524             continue;
525
526 #ifndef AFS_NAMEI_ENV
527         if (hasmntopt(&mnt, "logging") != NULL) {
528             Log("This program is compiled without AFS_NAMEI_ENV, and "
529                 "partition %s is mounted with the 'logging' option. "
530                 "Using the inode fileserver backend with 'logging' UFS "
531                 "partitions causes volume corruption, so please either "
532                 "mount the partition without logging, or use the namei "
533                 "fileserver backend. Aborting...\n", mnt.mnt_mountp);
534             errors++;
535         }
536 #endif /* !AFS_NAMEI_ENV */
537
538         if (VCheckPartition(mnt.mnt_mountp, mnt.mnt_special) < 0)
539             errors++;
540     }
541
542     (void)fclose(mntfile);
543
544     /* Process the always-attach partitions, if any. */
545     VAttachPartitions2();
546
547     return errors;
548 }
549
550 #endif /* AFS_SUN5_ENV */
551 #if defined(AFS_SGI_ENV) || (defined(AFS_SUN_ENV) && !defined(AFS_SUN5_ENV)) || defined(AFS_HPUX_ENV)
552 int
553 VAttachPartitions(void)
554 {
555     int errors = 0;
556     FILE *mfd;
557     struct mntent *mntent;
558
559     if ((mfd = setmntent(MOUNTED, "r")) == NULL) {
560         Log("Problems in getting mount entries(setmntent)\n");
561         exit(-1);
562     }
563     while (mntent = getmntent(mfd)) {
564         if (!hasmntopt(mntent, MNTOPT_RW))
565             continue;
566
567         /* If we're going to always attach this partition, do it later. */
568         if (VIsAlwaysAttach(mntent->mnt_dir, NULL))
569             continue;
570
571         if (VCheckPartition(mntent->mnt_dir, mntent->mnt_fsname) < 0)
572             errors++;
573     }
574
575     endmntent(mfd);
576
577     /* Process the always-attach partitions, if any. */
578     VAttachPartitions2();
579
580     return errors;
581 }
582 #endif
583 #ifdef AFS_AIX_ENV
584 /*
585  * (This function was grabbed from df.c)
586  */
587 int
588 getmount(struct vmount **vmountpp)
589 {
590     int size;
591     struct vmount *vm;
592     int nmounts;
593
594     /* set initial size of mntctl buffer to a MAGIC NUMBER */
595     size = BUFSIZ;
596
597     /* try the operation until ok or a fatal error */
598     while (1) {
599         if ((vm = (struct vmount *)malloc(size)) == NULL) {
600             /* failed getting memory for mount status buf */
601             perror("FATAL ERROR: get_stat malloc failed\n");
602             exit(-1);
603         }
604
605         /*
606          * perform the QUERY mntctl - if it returns > 0, that is the
607          * number of vmount structures in the buffer.  If it returns
608          * -1, an error occured.  If it returned 0, then look in
609          * first word of buffer for needed size.
610          */
611         if ((nmounts = mntctl(MCTL_QUERY, size, (caddr_t) vm)) > 0) {
612             /* OK, got it, now return */
613             *vmountpp = vm;
614             return (nmounts);
615
616         } else if (nmounts == 0) {
617             /* the buffer wasn't big enough .... */
618             /* .... get required buffer size */
619             size = *(int *)vm;
620             free(vm);
621
622         } else {
623             /* some other kind of error occurred */
624             free(vm);
625             return (-1);
626         }
627     }
628 }
629
630 int
631 VAttachPartitions(void)
632 {
633     int errors = 0;
634     int nmounts;
635     struct vmount *vmountp;
636
637     if ((nmounts = getmount(&vmountp)) <= 0) {
638         Log("Problems in getting # of mount entries(getmount)\n");
639         exit(-1);
640     }
641     for (; nmounts;
642          nmounts--, vmountp =
643          (struct vmount *)((int)vmountp + vmountp->vmt_length)) {
644         char *part = vmt2dataptr(vmountp, VMT_STUB);
645
646         if (vmountp->vmt_flags & (MNT_READONLY | MNT_REMOVABLE | MNT_REMOTE))
647             continue;           /* Ignore any "special" partitions */
648
649 #ifdef AFS_AIX42_ENV
650 #ifndef AFS_NAMEI_ENV
651         {
652             struct superblock fs;
653             /* The Log statements are non-sequiters in the SalvageLog and don't
654              * even appear in the VolserLog, so restrict them to the FileLog.
655              */
656             if (ReadSuper(&fs, vmt2dataptr(vmountp, VMT_OBJECT)) < 0) {
657                 if (programType == fileServer)
658                     Log("Can't read superblock for %s, ignoring it.\n", part);
659                 continue;
660             }
661             if (IsBigFilesFileSystem(&fs)) {
662                 if (programType == fileServer)
663                     Log("%s is a big files filesystem, ignoring it.\n", part);
664                 continue;
665             }
666         }
667 #endif
668 #endif
669
670         /* If we're going to always attach this partition, do it later. */
671         if (VIsAlwaysAttach(part, NULL))
672             continue;
673
674         if (VCheckPartition(part, vmt2dataptr(vmountp, VMT_OBJECT)) < 0)
675             errors++;
676     }
677
678     /* Process the always-attach partitions, if any. */
679     VAttachPartitions2();
680
681     return errors;
682 }
683 #endif
684 #if defined(AFS_DUX40_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
685 int
686 VAttachPartitions(void)
687 {
688     int errors = 0;
689     struct fstab *fsent;
690
691     if (setfsent() < 0) {
692         Log("Error listing filesystems.\n");
693         exit(-1);
694     }
695
696     while ((fsent = getfsent())) {
697         if (strcmp(fsent->fs_type, "rw") != 0)
698             continue;
699
700         /* If we're going to always attach this partition, do it later. */
701         if (VIsAlwaysAttach(fsent->fs_file, NULL))
702             continue;
703
704         if (VCheckPartition(fsent->fs_file, fsent->fs_spec) < 0)
705             errors++;
706     }
707     endfsent();
708
709     /* Process the always-attach partitions, if any. */
710     VAttachPartitions2();
711
712     return errors;
713 }
714 #endif
715
716 #ifdef AFS_NT40_ENV
717 #include <string.h>
718 #include <sys/stat.h>
719 /* VValidVPTEntry
720  *
721  * validate names in vptab.
722  *
723  * Return value:
724  * 1 valid entry
725  * 0 invalid entry
726  */
727
728 int
729 VValidVPTEntry(struct vptab *vpe)
730 {
731     int len = strlen(vpe->vp_name);
732     int i;
733
734     if (len < VICE_PREFIX_SIZE + 1 || len > VICE_PREFIX_SIZE + 2)
735         return 0;
736     if (strncmp(vpe->vp_name, VICE_PARTITION_PREFIX, VICE_PREFIX_SIZE))
737         return 0;
738
739     for (i = VICE_PREFIX_SIZE; i < len; i++) {
740         if (vpe->vp_name[i] < 'a' || vpe->vp_name[i] > 'z') {
741             Log("Invalid partition name %s in registry, ignoring it.\n",
742                 vpe->vp_name);
743             return 0;
744         }
745     }
746     if (len == VICE_PREFIX_SIZE + 2) {
747         i = (int)(vpe->vp_name[VICE_PREFIX_SIZE] - 'a') * 26 +
748             (int)(vpe->vp_name[VICE_PREFIX_SIZE + 1] - 'a');
749         if (i > 255) {
750             Log("Invalid partition name %s in registry, ignoring it.\n",
751                 vpe->vp_name);
752             return 0;
753         }
754     }
755
756     len = strlen(vpe->vp_dev);
757     if (len != 2 || vpe->vp_dev[1] != ':' || vpe->vp_dev[0] < 'A'
758         || vpe->vp_dev[0] > 'Z') {
759         Log("Invalid device name %s in registry, ignoring it.\n",
760             vpe->vp_dev);
761         return 0;
762     }
763
764     return 1;
765 }
766
767 int
768 VCheckPartition(char *partName)
769 {
770     char volRoot[4];
771     char volFsType[64];
772     DWORD dwDummy;
773     int err;
774
775     /* partName is presumed to be of the form "X:" */
776     (void)sprintf(volRoot, "%c:\\", *partName);
777
778     if (!GetVolumeInformation(volRoot,  /* volume root directory */
779                               NULL,     /* volume name buffer */
780                               0,        /* volume name size */
781                               NULL,     /* volume serial number */
782                               &dwDummy, /* max component length */
783                               &dwDummy, /* file system flags */
784                               volFsType,        /* file system name */
785                               sizeof(volFsType))) {
786         err = GetLastError();
787         Log("VCheckPartition: Failed to get partition information for %s, ignoring it.\n", partName);
788         return -1;
789     }
790
791     if (strcmp(volFsType, "NTFS")) {
792         Log("VCheckPartition: Partition %s is not an NTFS partition, ignoring it.\n", partName);
793         return -1;
794     }
795
796     return 0;
797 }
798
799
800 int
801 VAttachPartitions(void)
802 {
803     struct DiskPartition64 *partP, *prevP, *nextP;
804     struct vpt_iter iter;
805     struct vptab entry;
806
807     if (vpt_Start(&iter) < 0) {
808         Log("No partitions to attach.\n");
809         return 0;
810     }
811
812     while (0 == vpt_NextEntry(&iter, &entry)) {
813         if (!VValidVPTEntry(&entry)) {
814             continue;
815         }
816
817         /* This test for duplicates relies on the fact that the method
818          * of storing the partition names in the NT registry means the same
819          * partition name will never appear twice in the list.
820          */
821         for (partP = DiskPartitionList; partP; partP = partP->next) {
822             if (*partP->devName == *entry.vp_dev) {
823                 Log("Same drive (%s) used for both partition %s and partition %s, ignoring both.\n", entry.vp_dev, partP->name, entry.vp_name);
824                 partP->flags = PART_DUPLICATE;
825                 break;          /* Only one entry will ever be in this list. */
826             }
827         }
828         if (partP)
829             continue;           /* found a duplicate */
830
831         if (VCheckPartition(entry.vp_dev) < 0)
832             continue;
833         /* This test allows for manually inserting the FORCESALVAGE flag
834          * and thereby invoking the salvager. scandisk obviously won't be
835          * doing this for us.
836          */
837         if (programType == fileServer) {
838             struct afs_stat status;
839             char salvpath[MAXPATHLEN];
840             strcpy(salvpath, entry.vp_dev);
841             strcat(salvpath, "\\FORCESALVAGE");
842             if (afs_stat(salvpath, &status) == 0) {
843                 Log("VAttachPartitions: Found %s; aborting\n", salvpath);
844                 exit(1);
845             }
846         }
847         VInitPartition(entry.vp_name, entry.vp_dev, *entry.vp_dev - 'A');
848     }
849     vpt_Finish(&iter);
850
851     /* Run through partition list and clear out the dupes. */
852     prevP = nextP = NULL;
853     for (partP = DiskPartitionList; partP; partP = nextP) {
854         nextP = partP->next;
855         if (partP->flags == PART_DUPLICATE) {
856             if (prevP)
857                 prevP->next = partP->next;
858             else
859                 DiskPartitionList = partP->next;
860             free(partP);
861         } else
862             prevP = partP;
863     }
864
865     return 0;
866 }
867 #endif
868
869 #ifdef AFS_LINUX22_ENV
870 int
871 VAttachPartitions(void)
872 {
873     int errors = 0;
874     FILE *mfd;
875     struct mntent *mntent;
876
877     if ((mfd = setmntent("/proc/mounts", "r")) == NULL) {
878         if ((mfd = setmntent("/etc/mtab", "r")) == NULL) {
879             Log("Problems in getting mount entries(setmntent)\n");
880             exit(-1);
881         }
882     }
883     while ((mntent = getmntent(mfd))) {
884         /* If we're going to always attach this partition, do it later. */
885         if (VIsAlwaysAttach(mntent->mnt_dir, NULL))
886             continue;
887
888         if (VCheckPartition(mntent->mnt_dir, mntent->mnt_fsname) < 0)
889             errors++;
890     }
891     endmntent(mfd);
892
893     /* Process the always-attach partitions, if any. */
894     VAttachPartitions2();
895
896     return errors;
897 }
898 #endif /* AFS_LINUX22_ENV */
899
900 /* This routine is to be called whenever the actual name of the partition
901  * is required. The canonical name is still in part->name.
902  */
903 char *
904 VPartitionPath(struct DiskPartition64 *part)
905 {
906 #ifdef AFS_NT40_ENV
907     return part->devName;
908 #else
909     return part->name;
910 #endif
911 }
912
913 /* get partition structure, abortp tells us if we should abort on failure */
914 struct DiskPartition64 *
915 VGetPartition_r(char *name, int abortp)
916 {
917     struct DiskPartition64 *dp;
918 #ifdef AFS_DEMAND_ATTACH_FS
919     dp = VLookupPartition_r(name);
920 #else /* AFS_DEMAND_ATTACH_FS */
921     for (dp = DiskPartitionList; dp; dp = dp->next) {
922         if (strcmp(dp->name, name) == 0)
923             break;
924     }
925 #endif /* AFS_DEMAND_ATTACH_FS */
926     if (abortp)
927         assert(dp != NULL);
928     return dp;
929 }
930
931 struct DiskPartition64 *
932 VGetPartition(char *name, int abortp)
933 {
934     struct DiskPartition64 *retVal;
935     VOL_LOCK;
936     retVal = VGetPartition_r(name, abortp);
937     VOL_UNLOCK;
938     return retVal;
939 }
940
941 #ifdef AFS_NT40_ENV
942 void
943 VSetPartitionDiskUsage_r(struct DiskPartition64 *dp)
944 {
945     ULARGE_INTEGER free_user, total, free_total;
946     int ufree, tot, tfree;
947
948     if (!GetDiskFreeSpaceEx
949         (VPartitionPath(dp), &free_user, &total, &free_total)) {
950         printf("Failed to get disk space info for %s, error = %d\n", dp->name,
951                GetLastError());
952         return;
953     }
954
955     /* Convert to 1K units. */
956     ufree = (int)Int64ShraMod32(free_user.QuadPart, 10);
957     tot = (int)Int64ShraMod32(total.QuadPart, 10);
958     tfree = (int)Int64ShraMod32(free_total.QuadPart, 10);
959
960     dp->minFree = tfree - ufree;        /* only used in VPrintDiskStats_r */
961     dp->totalUsable = tot;
962     dp->free = tfree;
963 }
964
965 #else
966 void
967 VSetPartitionDiskUsage_r(struct DiskPartition64 *dp)
968 {
969     int bsize, code;
970     afs_int64 totalblks, free, used, availblks;
971     int reserved;
972 #ifdef afs_statvfs
973     struct afs_statvfs statbuf;
974 #else
975     struct afs_statfs statbuf;
976 #endif
977
978     if (dp->flags & PART_DONTUPDATE)
979         return;
980     /* Note:  we don't bother syncing because it's only an estimate, update
981      * is syncing every 30 seconds anyway, we only have to keep the disk
982      * approximately 10% from full--you just can't get the stuff in from
983      * the net fast enough to worry */
984 #ifdef afs_statvfs
985     code = afs_statvfs(dp->name, &statbuf);
986 #else
987     code = afs_statfs(dp->name, &statbuf);
988 #endif
989     if (code < 0) {
990         Log("statfs of %s failed in VSetPartitionDiskUsage (errno = %d)\n",
991             dp->name, errno);
992         return;
993     }
994     if (statbuf.f_blocks == -1) {       /* Undefined; skip stats.. */
995         Log("statfs of %s failed in VSetPartitionDiskUsage\n", dp->name);
996         return;
997     }
998     totalblks = statbuf.f_blocks;
999     free = statbuf.f_bfree;
1000     reserved = free - statbuf.f_bavail;
1001 #ifdef afs_statvfs
1002     bsize = statbuf.f_frsize;
1003 #else
1004     bsize = statbuf.f_bsize;
1005 #endif
1006     availblks = totalblks - reserved;
1007     dp->f_files = statbuf.f_files;      /* max # of files in partition */
1008
1009     /* Now free and totalblks are in fragment units, but we want them in
1010      * 1K units.
1011      */
1012     if (bsize >= 1024) {
1013         free *= (bsize / 1024);
1014         totalblks *= (bsize / 1024);
1015         availblks *= (bsize / 1024);
1016         reserved *= (bsize / 1024);
1017     } else {
1018         free /= (1024 / bsize);
1019         totalblks /= (1024 / bsize);
1020         availblks /= (1024 / bsize);
1021         reserved /= (1024 / bsize);
1022     }
1023     /* now compute remaining figures */
1024     used = totalblks - free;
1025
1026     dp->minFree = reserved;     /* only used in VPrintDiskStats_r */
1027     dp->totalUsable = availblks;
1028     dp->free = availblks - used;        /* this is exactly f_bavail */
1029 }
1030 #endif /* AFS_NT40_ENV */
1031
1032 void
1033 VSetPartitionDiskUsage(struct DiskPartition64 *dp)
1034 {
1035     VOL_LOCK;
1036     VSetPartitionDiskUsage_r(dp);
1037     VOL_UNLOCK;
1038 }
1039
1040 void
1041 VResetDiskUsage_r(void)
1042 {
1043     struct DiskPartition64 *dp;
1044     for (dp = DiskPartitionList; dp; dp = dp->next) {
1045         VSetPartitionDiskUsage_r(dp);
1046 #ifndef AFS_PTHREAD_ENV
1047         IOMGR_Poll();
1048 #endif /* !AFS_PTHREAD_ENV */
1049     }
1050 }
1051
1052 void
1053 VResetDiskUsage(void)
1054 {
1055     VOL_LOCK;
1056     VResetDiskUsage_r();
1057     VOL_UNLOCK;
1058 }
1059
1060 void
1061 VAdjustDiskUsage_r(Error * ec, Volume * vp, afs_sfsize_t blocks,
1062                    afs_sfsize_t checkBlocks)
1063 {
1064     *ec = 0;
1065     /* why blocks instead of checkBlocks in the check below?  Otherwise, any check
1066      * for less than BlocksSpare would skip the error-checking path, and we
1067      * could grow existing files forever, not just for another BlocksSpare
1068      * blocks. */
1069     if (blocks > 0) {
1070 #ifdef  AFS_AIX32_ENV
1071         afs_int32 rem, minavail;
1072
1073         if ((rem = vp->partition->free - checkBlocks) < (minavail =
1074                                                          (vp->partition->
1075                                                           totalUsable *
1076                                                           aixlow_water) /
1077                                                          100))
1078 #else
1079         if (vp->partition->free - checkBlocks < 0)
1080 #endif
1081             *ec = VDISKFULL;
1082         else if (V_maxquota(vp)
1083                  && V_diskused(vp) + checkBlocks > V_maxquota(vp))
1084             *ec = VOVERQUOTA;
1085     }
1086     vp->partition->free -= blocks;
1087     V_diskused(vp) += blocks;
1088 }
1089
1090 void
1091 VAdjustDiskUsage(Error * ec, Volume * vp, afs_sfsize_t blocks,
1092                  afs_sfsize_t checkBlocks)
1093 {
1094     VOL_LOCK;
1095     VAdjustDiskUsage_r(ec, vp, blocks, checkBlocks);
1096     VOL_UNLOCK;
1097 }
1098
1099 int
1100 VDiskUsage_r(Volume * vp, afs_sfsize_t blocks)
1101 {
1102     if (blocks > 0) {
1103 #ifdef  AFS_AIX32_ENV
1104         afs_int32 rem, minavail;
1105
1106         if ((rem = vp->partition->free - blocks) < (minavail =
1107                                                     (vp->partition->
1108                                                      totalUsable *
1109                                                      aixlow_water) / 100))
1110 #else
1111         if (vp->partition->free - blocks < 0)
1112 #endif
1113             return (VDISKFULL);
1114     }
1115     vp->partition->free -= blocks;
1116     return 0;
1117 }
1118
1119 int
1120 VDiskUsage(Volume * vp, afs_sfsize_t blocks)
1121 {
1122     int retVal;
1123     VOL_LOCK;
1124     retVal = VDiskUsage_r(vp, blocks);
1125     VOL_UNLOCK;
1126     return retVal;
1127 }
1128
1129 void
1130 VPrintDiskStats_r(void)
1131 {
1132     struct DiskPartition64 *dp;
1133     for (dp = DiskPartitionList; dp; dp = dp->next) {
1134         if (dp->free < 0) {
1135             Log("Partition %s: %"AFS_INT64_FMT
1136                 " available 1K blocks (minfree=%"AFS_INT64_FMT"), "
1137                 "overallocated by %"AFS_INT64_FMT" blocks\n", dp->name,
1138                 dp->totalUsable, dp->minFree, -dp->free);
1139         } else {
1140             Log("Partition %s: %"AFS_INT64_FMT
1141                 " available 1K blocks (minfree=%"AFS_INT64_FMT"), "
1142                 "%"AFS_INT64_FMT" free blocks\n", dp->name,
1143                 dp->totalUsable, dp->minFree, dp->free);
1144         }
1145     }
1146 }
1147
1148 void
1149 VPrintDiskStats(void)
1150 {
1151     VOL_LOCK;
1152     VPrintDiskStats_r();
1153     VOL_UNLOCK;
1154 }
1155
1156 #ifdef AFS_NT40_ENV
1157 /* Need a separate lock file on NT, since NT only has mandatory file locks. */
1158 #define LOCKFILE "LOCKFILE"
1159 void
1160 VLockPartition_r(char *name)
1161 {
1162     struct DiskPartition64 *dp = VGetPartition_r(name, 0);
1163     OVERLAPPED lap;
1164
1165     if (!dp)
1166         return;
1167     if (dp->lock_fd == INVALID_FD) {
1168         char path[64];
1169         int rc;
1170         (void)sprintf(path, "%s\\%s", VPartitionPath(dp), LOCKFILE);
1171         dp->lock_fd =
1172             (FD_t)CreateFile(path, GENERIC_WRITE,
1173                             FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1174                             CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL);
1175         assert(dp->lock_fd != INVALID_FD);
1176
1177         memset(&lap, 0, sizeof(lap));
1178         rc = LockFileEx((HANDLE) dp->lock_fd, LOCKFILE_EXCLUSIVE_LOCK, 0, 1,
1179                         0, &lap);
1180         assert(rc);
1181     }
1182 }
1183
1184 void
1185 VUnlockPartition_r(char *name)
1186 {
1187     struct DiskPartition64 *dp = VGetPartition_r(name, 0);
1188     OVERLAPPED lap;
1189
1190     if (!dp)
1191         return;                 /* no partition, will fail later */
1192     memset(&lap, 0, sizeof(lap));
1193
1194     UnlockFileEx((HANDLE) dp->lock_fd, 0, 1, 0, &lap);
1195     CloseHandle((HANDLE) dp->lock_fd);
1196     dp->lock_fd = INVALID_FD;
1197 }
1198 #else /* AFS_NT40_ENV */
1199
1200 #if defined(AFS_HPUX_ENV)
1201 #define BITS_PER_CHAR   (8)
1202 #define BITS(type)      (sizeof(type) * BITS_PER_CHAR)
1203
1204 #define LOCKRDONLY_OFFSET       ((PRIV_LOCKRDONLY - 1) / BITS(int))
1205 #endif /* defined(AFS_HPUX_ENV) */
1206
1207 void
1208 VLockPartition_r(char *name)
1209 {
1210     struct DiskPartition64 *dp = VGetPartition_r(name, 0);
1211     char *partitionName;
1212     int retries, code;
1213     struct timeval pausing;
1214 #if defined(AFS_HPUX_ENV)
1215     int lockfRtn;
1216     struct privgrp_map privGrpList[PRIV_MAXGRPS];
1217     unsigned int *globalMask;
1218     int globalMaskIndex;
1219 #endif /* defined(AFS_HPUX_ENV) */
1220 #if defined(AFS_DARWIN_ENV)
1221     char lockfile[MAXPATHLEN];
1222 #endif /* defined(AFS_DARWIN_ENV) */
1223 #ifdef AFS_NAMEI_ENV
1224 #ifdef AFS_AIX42_ENV
1225     char LockFileName[MAXPATHLEN + 1];
1226
1227     sprintf((char *)&LockFileName, "%s/AFSINODE_FSLock", name);
1228     partitionName = (char *)&LockFileName;
1229 #endif
1230 #endif
1231
1232     if (!dp)
1233         return;                 /* no partition, will fail later */
1234     if (dp->lock_fd != -1)
1235         return;
1236
1237 #if    defined(AFS_SUN5_ENV) || defined(AFS_AIX41_ENV)
1238 #if !defined(AFS_AIX42_ENV) || !defined(AFS_NAMEI_ENV)
1239     partitionName = dp->devName;
1240 #endif
1241     code = O_RDWR;
1242 #elif defined(AFS_DARWIN_ENV)
1243     strlcpy((partitionName = lockfile), dp->name, sizeof(lockfile));
1244     strlcat(lockfile, "/.lock.afs", sizeof(lockfile));
1245     code = O_RDONLY | O_CREAT;
1246 #else
1247     partitionName = dp->name;
1248     code = O_RDONLY;
1249 #endif
1250
1251     for (retries = 25; retries; retries--) {
1252         if (code & O_CREAT)
1253             dp->lock_fd = afs_open(partitionName, code, 0644);
1254         else
1255             dp->lock_fd = afs_open(partitionName, code);
1256
1257         if (dp->lock_fd != -1)
1258             break;
1259         if (errno == ENOENT)
1260             code |= O_CREAT;
1261         pausing.tv_sec = 0;
1262         pausing.tv_usec = 500000;
1263         select(0, NULL, NULL, NULL, &pausing);
1264     }
1265     assert(retries != 0);
1266
1267 #if defined (AFS_HPUX_ENV)
1268
1269     assert(getprivgrp(privGrpList) == 0);
1270
1271     /*
1272      * In general, it will difficult and time-consuming ,if not impossible,
1273      * to try to find the privgroup to which this process belongs that has the
1274      * smallest membership, to minimise the security hole.  So, we use the privgrp
1275      * to which everybody belongs.
1276      */
1277     /* first, we have to find the global mask */
1278     for (globalMaskIndex = 0; globalMaskIndex < PRIV_MAXGRPS;
1279          globalMaskIndex++) {
1280         if (privGrpList[globalMaskIndex].priv_groupno == PRIV_GLOBAL) {
1281             globalMask =
1282                 &(privGrpList[globalMaskIndex].priv_mask[LOCKRDONLY_OFFSET]);
1283             break;
1284         }
1285     }
1286
1287     if (((*globalMask) & privmask(PRIV_LOCKRDONLY)) == 0) {
1288         /* allow everybody to set a lock on a read-only file descriptor */
1289         (*globalMask) |= privmask(PRIV_LOCKRDONLY);
1290         assert(setprivgrp(PRIV_GLOBAL, privGrpList[globalMaskIndex].priv_mask)
1291                == 0);
1292
1293         lockfRtn = lockf(dp->lock_fd, F_LOCK, 0);
1294
1295         /* remove the privilege granted to everybody to lock a read-only fd */
1296         (*globalMask) &= ~(privmask(PRIV_LOCKRDONLY));
1297         assert(setprivgrp(PRIV_GLOBAL, privGrpList[globalMaskIndex].priv_mask)
1298                == 0);
1299     } else {
1300         /* in this case, we should be able to do this with impunity, anyway */
1301         lockfRtn = lockf(dp->lock_fd, F_LOCK, 0);
1302     }
1303
1304     assert(lockfRtn != -1);
1305 #else
1306 #if defined(AFS_AIX_ENV) || defined(AFS_SUN5_ENV)
1307     assert(lockf(dp->lock_fd, F_LOCK, 0) != -1);
1308 #else
1309     assert(flock(dp->lock_fd, LOCK_EX) == 0);
1310 #endif /* defined(AFS_AIX_ENV) || defined(AFS_SUN5_ENV) */
1311 #endif
1312 }
1313
1314 void
1315 VUnlockPartition_r(char *name)
1316 {
1317     struct DiskPartition64 *dp = VGetPartition_r(name, 0);
1318     if (!dp)
1319         return;                 /* no partition, will fail later */
1320     close(dp->lock_fd);
1321     dp->lock_fd = -1;
1322 }
1323
1324 #endif /* AFS_NT40_ENV */
1325
1326 void
1327 VLockPartition(char *name)
1328 {
1329     VOL_LOCK;
1330     VLockPartition_r(name);
1331     VOL_UNLOCK;
1332 }
1333
1334 void
1335 VUnlockPartition(char *name)
1336 {
1337     VOL_LOCK;
1338     VUnlockPartition_r(name);
1339     VOL_UNLOCK;
1340 }
1341
1342 #ifdef AFS_DEMAND_ATTACH_FS
1343
1344 /* new-style partition locks; these are only to have some mutual exclusion
1345  * between the VGC scanner and volume utilies creating/altering vol headers
1346  */
1347
1348 /**
1349  * lock a partition's vol headers.
1350  *
1351  * @param[in] dp       the partition to lock
1352  * @param[in] locktype READ_LOCK or WRITE_LOCK
1353  *
1354  * @return operation status
1355  *  @retval 0 success
1356  */
1357 int
1358 VPartHeaderLock(struct DiskPartition64 *dp, int locktype)
1359 {
1360     int code;
1361
1362     /* block on acquiring the lock */
1363     int nonblock = 0;
1364
1365     code = VGetDiskLock(&dp->headerLock, locktype, nonblock);
1366     if (code) {
1367         Log("VPartHeaderLock: error %d locking partititon %s\n", code,
1368             VPartitionPath(dp));
1369     }
1370     return code;
1371 }
1372
1373 /**
1374  * unlock a partition's vol headers.
1375  *
1376  * @param[in] dp       the partition to unlock
1377  * @param[in] locktype READ_LOCK or WRITE_LOCK
1378  */
1379 void
1380 VPartHeaderUnlock(struct DiskPartition64 *dp, int locktype)
1381 {
1382     VReleaseDiskLock(&dp->headerLock, locktype);
1383 }
1384
1385 /* XXX not sure this will work on AFS_NT40_ENV
1386  * needs to be tested!
1387  */
1388
1389 /**
1390  * lookup a disk partition object by its index number.
1391  *
1392  * @param[in] id      partition index number
1393  * @param[in] abortp  see abortp usage note below
1394  *
1395  * @return disk partition object
1396  *   @retval NULL no such disk partition
1397  *
1398  * @note when abortp is non-zero, lookups which would return
1399  *       NULL will result in an assertion failure
1400  *
1401  * @pre VOL_LOCK must be held
1402  *
1403  * @internal volume package internal use only
1404  */
1405
1406 struct DiskPartition64 *
1407 VGetPartitionById_r(afs_int32 id, int abortp)
1408 {
1409     struct DiskPartition64 *dp = NULL;
1410
1411     if ((id >= 0) && (id <= VOLMAXPARTS)) {
1412         dp = DiskPartitionTable[id];
1413     }
1414
1415     if (abortp) {
1416         assert(dp != NULL);
1417     }
1418     return dp;
1419 }
1420
1421 /**
1422  * lookup a disk partition object by its index number.
1423  *
1424  * @param[in] id      partition index number
1425  * @param[in] abortp  see abortp usage note below
1426  *
1427  * @return disk partition object
1428  *   @retval NULL no such disk partition
1429  *
1430  * @note when abortp is non-zero, lookups which would return
1431  *       NULL will result in an assertion failure
1432  */
1433
1434 struct DiskPartition64 *
1435 VGetPartitionById(afs_int32 id, int abortp)
1436 {
1437     struct DiskPartition64 * dp;
1438
1439     VOL_LOCK;
1440     dp = VGetPartitionById_r(id, abortp);
1441     VOL_UNLOCK;
1442
1443     return dp;
1444 }
1445
1446 static struct DiskPartition64 *
1447 VLookupPartition_r(char * path)
1448 {
1449     afs_int32 id = volutil_GetPartitionID(path);
1450
1451     if (id < 0 || id > VOLMAXPARTS)
1452         return NULL;
1453
1454     return DiskPartitionTable[id];
1455 }
1456
1457 static void
1458 AddPartitionToTable_r(struct DiskPartition64 *dp)
1459 {
1460     assert(dp->index >= 0 && dp->index <= VOLMAXPARTS);
1461     DiskPartitionTable[dp->index] = dp;
1462 }
1463
1464 #if 0
1465 static void
1466 DeletePartitionFromTable_r(struct DiskPartition64 *dp)
1467 {
1468     assert(dp->index >= 0 && dp->index <= VOLMAXPARTS);
1469     DiskPartitionTable[dp->index] = NULL;
1470 }
1471 #endif
1472 #endif /* AFS_DEMAND_ATTACH_FS */