salvage: Zero root/readme vnodes before writing
[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/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 static int
406 VIsAlwaysAttach(char *part)
407 {
408 #ifdef AFS_NAMEI_ENV
409     struct afs_stat st;
410     char checkfile[256];
411     int ret;
412
413     if (strncmp(part, VICE_PARTITION_PREFIX, VICE_PREFIX_SIZE))
414         return 0;
415
416     strncpy(checkfile, part, 100);
417     strcat(checkfile, "/");
418     strcat(checkfile, VICE_ALWAYSATTACH_FILE);
419
420     ret = afs_stat(checkfile, &st);
421     return (ret < 0) ? 0 : 1;
422 #else /* AFS_NAMEI_ENV */
423     return 0;
424 #endif /* AFS_NAMEI_ENV */
425 }
426
427 /* VAttachPartitions2() looks for and attaches /vicepX partitions
428  * where a special file (VICE_ALWAYSATTACH_FILE) exists.  This is
429  * used to attach /vicepX directories which aren't on dedicated
430  * partitions, in the NAMEI fileserver.
431  */
432 void
433 VAttachPartitions2(void)
434 {
435 #ifdef AFS_NAMEI_ENV
436     DIR *dirp;
437     struct dirent *de;
438     char pname[32];
439
440     dirp = opendir("/");
441     while ((de = readdir(dirp))) {
442         strcpy(pname, "/");
443         strncat(pname, de->d_name, 20);
444         pname[sizeof(pname) - 1] = '\0';
445
446         /* Only keep track of "/vicepx" partitions since automounter
447          * may hose us */
448         if (VIsAlwaysAttach(pname))
449             VCheckPartition(pname, "");
450     }
451     closedir(dirp);
452 #endif /* AFS_NAMEI_ENV */
453 }
454 #endif /* AFS_NT40_ENV */
455
456 #ifdef AFS_SUN5_ENV
457 int
458 VAttachPartitions(void)
459 {
460     int errors = 0;
461     struct mnttab mnt;
462     FILE *mntfile;
463
464     if (!(mntfile = afs_fopen(MNTTAB, "r"))) {
465         Log("Can't open %s\n", MNTTAB);
466         perror(MNTTAB);
467         exit(-1);
468     }
469     while (!getmntent(mntfile, &mnt)) {
470         /* Ignore non ufs or non read/write partitions */
471         /* but allow zfs too if we're in the NAMEI environment */
472         if (
473 #ifdef AFS_NAMEI_ENV
474             (((strcmp(mnt.mnt_fstype, "ufs") &&
475                 strcmp(mnt.mnt_fstype, "zfs"))))
476 #else
477             (strcmp(mnt.mnt_fstype, "ufs") != 0)
478 #endif
479             || (strncmp(mnt.mnt_mntopts, "ro,ignore", 9) == 0))
480             continue;
481         
482         /* If we're going to always attach this partition, do it later. */
483         if (VIsAlwaysAttach(mnt.mnt_mountp))
484             continue;
485
486 #ifndef AFS_NAMEI_ENV
487         if (hasmntopt(&mnt, "logging") != NULL) {
488             Log("This program is compiled without AFS_NAMEI_ENV, and "
489                 "partition %s is mounted with the 'logging' option. "
490                 "Using the inode fileserver backend with 'logging' UFS "
491                 "partitions causes volume corruption, so please either "
492                 "mount the partition without logging, or use the namei "
493                 "fileserver backend. Aborting...\n", mnt.mnt_mountp);
494             errors++;
495         }
496 #endif /* !AFS_NAMEI_ENV */
497
498         if (VCheckPartition(mnt.mnt_mountp, mnt.mnt_special) < 0)
499             errors++;
500     }
501
502     (void)fclose(mntfile);
503
504     /* Process the always-attach partitions, if any. */
505     VAttachPartitions2();
506
507     return errors;
508 }
509
510 #endif /* AFS_SUN5_ENV */
511 #if defined(AFS_SGI_ENV) || (defined(AFS_SUN_ENV) && !defined(AFS_SUN5_ENV)) || defined(AFS_HPUX_ENV)
512 int
513 VAttachPartitions(void)
514 {
515     int errors = 0;
516     FILE *mfd;
517     struct mntent *mntent;
518
519     if ((mfd = setmntent(MOUNTED, "r")) == NULL) {
520         Log("Problems in getting mount entries(setmntent)\n");
521         exit(-1);
522     }
523     while (mntent = getmntent(mfd)) {
524         if (!hasmntopt(mntent, MNTOPT_RW))
525             continue;
526
527         /* If we're going to always attach this partition, do it later. */
528         if (VIsAlwaysAttach(mntent->mnt_dir))
529             continue;
530
531         if (VCheckPartition(mntent->mnt_dir, mntent->mnt_fsname) < 0)
532             errors++;
533     }
534
535     endmntent(mfd);
536
537     /* Process the always-attach partitions, if any. */
538     VAttachPartitions2();
539
540     return errors;
541 }
542 #endif
543 #ifdef AFS_AIX_ENV
544 /*
545  * (This function was grabbed from df.c)
546  */
547 int
548 getmount(register struct vmount **vmountpp)
549 {
550     int size;
551     register struct vmount *vm;
552     int nmounts;
553
554     /* set initial size of mntctl buffer to a MAGIC NUMBER */
555     size = BUFSIZ;
556
557     /* try the operation until ok or a fatal error */
558     while (1) {
559         if ((vm = (struct vmount *)malloc(size)) == NULL) {
560             /* failed getting memory for mount status buf */
561             perror("FATAL ERROR: get_stat malloc failed\n");
562             exit(-1);
563         }
564
565         /*
566          * perform the QUERY mntctl - if it returns > 0, that is the
567          * number of vmount structures in the buffer.  If it returns
568          * -1, an error occured.  If it returned 0, then look in
569          * first word of buffer for needed size.
570          */
571         if ((nmounts = mntctl(MCTL_QUERY, size, (caddr_t) vm)) > 0) {
572             /* OK, got it, now return */
573             *vmountpp = vm;
574             return (nmounts);
575
576         } else if (nmounts == 0) {
577             /* the buffer wasn't big enough .... */
578             /* .... get required buffer size */
579             size = *(int *)vm;
580             free(vm);
581
582         } else {
583             /* some other kind of error occurred */
584             free(vm);
585             return (-1);
586         }
587     }
588 }
589
590 int
591 VAttachPartitions(void)
592 {
593     int errors = 0;
594     int nmounts;
595     struct vmount *vmountp;
596
597     if ((nmounts = getmount(&vmountp)) <= 0) {
598         Log("Problems in getting # of mount entries(getmount)\n");
599         exit(-1);
600     }
601     for (; nmounts;
602          nmounts--, vmountp =
603          (struct vmount *)((int)vmountp + vmountp->vmt_length)) {
604         char *part = vmt2dataptr(vmountp, VMT_STUB);
605
606         if (vmountp->vmt_flags & (MNT_READONLY | MNT_REMOVABLE | MNT_REMOTE))
607             continue;           /* Ignore any "special" partitions */
608
609 #ifdef AFS_AIX42_ENV
610 #ifndef AFS_NAMEI_ENV
611         {
612             struct superblock fs;
613             /* The Log statements are non-sequiters in the SalvageLog and don't
614              * even appear in the VolserLog, so restrict them to the FileLog.
615              */
616             if (ReadSuper(&fs, vmt2dataptr(vmountp, VMT_OBJECT)) < 0) {
617                 if (programType == fileServer)
618                     Log("Can't read superblock for %s, ignoring it.\n", part);
619                 continue;
620             }
621             if (IsBigFilesFileSystem(&fs)) {
622                 if (programType == fileServer)
623                     Log("%s is a big files filesystem, ignoring it.\n", part);
624                 continue;
625             }
626         }
627 #endif
628 #endif
629
630         /* If we're going to always attach this partition, do it later. */
631         if (VIsAlwaysAttach(part))
632             continue;
633
634         if (VCheckPartition(part, vmt2dataptr(vmountp, VMT_OBJECT)) < 0)
635             errors++;
636     }
637
638     /* Process the always-attach partitions, if any. */
639     VAttachPartitions2();
640
641     return errors;
642 }
643 #endif
644 #if defined(AFS_DUX40_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
645 int
646 VAttachPartitions(void)
647 {
648     int errors = 0;
649     struct fstab *fsent;
650
651     if (setfsent() < 0) {
652         Log("Error listing filesystems.\n");
653         exit(-1);
654     }
655
656     while ((fsent = getfsent())) {
657         if (strcmp(fsent->fs_type, "rw") != 0)
658             continue;
659
660         /* If we're going to always attach this partition, do it later. */
661         if (VIsAlwaysAttach(fsent->fs_file))
662             continue;
663
664         if (VCheckPartition(fsent->fs_file, fsent->fs_spec) < 0)
665             errors++;
666     }
667     endfsent();
668
669     /* Process the always-attach partitions, if any. */
670     VAttachPartitions2();
671
672     return errors;
673 }
674 #endif
675
676 #ifdef AFS_NT40_ENV
677 #include <string.h>
678 #include <sys/stat.h>
679 /* VValidVPTEntry
680  *
681  * validate names in vptab.
682  *
683  * Return value:
684  * 1 valid entry
685  * 0 invalid entry
686  */
687
688 int
689 VValidVPTEntry(struct vptab *vpe)
690 {
691     int len = strlen(vpe->vp_name);
692     int i;
693
694     if (len < VICE_PREFIX_SIZE + 1 || len > VICE_PREFIX_SIZE + 2)
695         return 0;
696     if (strncmp(vpe->vp_name, VICE_PARTITION_PREFIX, VICE_PREFIX_SIZE))
697         return 0;
698
699     for (i = VICE_PREFIX_SIZE; i < len; i++) {
700         if (vpe->vp_name[i] < 'a' || vpe->vp_name[i] > 'z') {
701             Log("Invalid partition name %s in registry, ignoring it.\n",
702                 vpe->vp_name);
703             return 0;
704         }
705     }
706     if (len == VICE_PREFIX_SIZE + 2) {
707         i = (int)(vpe->vp_name[VICE_PREFIX_SIZE] - 'a') * 26 +
708             (int)(vpe->vp_name[VICE_PREFIX_SIZE + 1] - 'a');
709         if (i > 255) {
710             Log("Invalid partition name %s in registry, ignoring it.\n",
711                 vpe->vp_name);
712             return 0;
713         }
714     }
715
716     len = strlen(vpe->vp_dev);
717     if (len != 2 || vpe->vp_dev[1] != ':' || vpe->vp_dev[0] < 'A'
718         || vpe->vp_dev[0] > 'Z') {
719         Log("Invalid device name %s in registry, ignoring it.\n",
720             vpe->vp_dev);
721         return 0;
722     }
723
724     return 1;
725 }
726
727 int
728 VCheckPartition(char *partName)
729 {
730     char volRoot[4];
731     char volFsType[64];
732     DWORD dwDummy;
733     int err;
734
735     /* partName is presumed to be of the form "X:" */
736     (void)sprintf(volRoot, "%c:\\", *partName);
737
738     if (!GetVolumeInformation(volRoot,  /* volume root directory */
739                               NULL,     /* volume name buffer */
740                               0,        /* volume name size */
741                               NULL,     /* volume serial number */
742                               &dwDummy, /* max component length */
743                               &dwDummy, /* file system flags */
744                               volFsType,        /* file system name */
745                               sizeof(volFsType))) {
746         err = GetLastError();
747         Log("VCheckPartition: Failed to get partition information for %s, ignoring it.\n", partName);
748         return -1;
749     }
750
751     if (strcmp(volFsType, "NTFS")) {
752         Log("VCheckPartition: Partition %s is not an NTFS partition, ignoring it.\n", partName);
753         return -1;
754     }
755
756     return 0;
757 }
758
759
760 int
761 VAttachPartitions(void)
762 {
763     struct DiskPartition64 *partP, *prevP, *nextP;
764     struct vpt_iter iter;
765     struct vptab entry;
766
767     if (vpt_Start(&iter) < 0) {
768         Log("No partitions to attach.\n");
769         return 0;
770     }
771
772     while (0 == vpt_NextEntry(&iter, &entry)) {
773         if (!VValidVPTEntry(&entry)) {
774             continue;
775         }
776
777         /* This test for duplicates relies on the fact that the method
778          * of storing the partition names in the NT registry means the same
779          * partition name will never appear twice in the list.
780          */
781         for (partP = DiskPartitionList; partP; partP = partP->next) {
782             if (*partP->devName == *entry.vp_dev) {
783                 Log("Same drive (%s) used for both partition %s and partition %s, ignoring both.\n", entry.vp_dev, partP->name, entry.vp_name);
784                 partP->flags = PART_DUPLICATE;
785                 break;          /* Only one entry will ever be in this list. */
786             }
787         }
788         if (partP)
789             continue;           /* found a duplicate */
790
791         if (VCheckPartition(entry.vp_dev) < 0)
792             continue;
793         /* This test allows for manually inserting the FORCESALVAGE flag
794          * and thereby invoking the salvager. scandisk obviously won't be
795          * doing this for us.
796          */
797         if (programType == fileServer) {
798             struct afs_stat status;
799             char salvpath[MAXPATHLEN];
800             strcpy(salvpath, entry.vp_dev);
801             strcat(salvpath, "\\FORCESALVAGE");
802             if (afs_stat(salvpath, &status) == 0) {
803                 Log("VAttachPartitions: Found %s; aborting\n", salvpath);
804                 exit(1);
805             }
806         }
807         VInitPartition(entry.vp_name, entry.vp_dev, *entry.vp_dev - 'A');
808     }
809     vpt_Finish(&iter);
810
811     /* Run through partition list and clear out the dupes. */
812     prevP = nextP = NULL;
813     for (partP = DiskPartitionList; partP; partP = nextP) {
814         nextP = partP->next;
815         if (partP->flags == PART_DUPLICATE) {
816             if (prevP)
817                 prevP->next = partP->next;
818             else
819                 DiskPartitionList = partP->next;
820             free(partP);
821         } else
822             prevP = partP;
823     }
824
825     return 0;
826 }
827 #endif
828
829 #ifdef AFS_LINUX22_ENV
830 int
831 VAttachPartitions(void)
832 {
833     int errors = 0;
834     FILE *mfd;
835     struct mntent *mntent;
836
837     if ((mfd = setmntent("/proc/mounts", "r")) == NULL) {
838         if ((mfd = setmntent("/etc/mtab", "r")) == NULL) {
839             Log("Problems in getting mount entries(setmntent)\n");
840             exit(-1);
841         }
842     }
843     while ((mntent = getmntent(mfd))) {
844         /* If we're going to always attach this partition, do it later. */
845         if (VIsAlwaysAttach(mntent->mnt_dir))
846             continue;
847
848         if (VCheckPartition(mntent->mnt_dir, mntent->mnt_fsname) < 0)
849             errors++;
850     }
851     endmntent(mfd);
852
853     /* Process the always-attach partitions, if any. */
854     VAttachPartitions2();
855
856     return errors;
857 }
858 #endif /* AFS_LINUX22_ENV */
859
860 /* This routine is to be called whenever the actual name of the partition
861  * is required. The canonical name is still in part->name.
862  */
863 char *
864 VPartitionPath(struct DiskPartition64 *part)
865 {
866 #ifdef AFS_NT40_ENV
867     return part->devName;
868 #else
869     return part->name;
870 #endif
871 }
872
873 /* get partition structure, abortp tells us if we should abort on failure */
874 struct DiskPartition64 *
875 VGetPartition_r(char *name, int abortp)
876 {
877     register struct DiskPartition64 *dp;
878 #ifdef AFS_DEMAND_ATTACH_FS
879     dp = VLookupPartition_r(name);
880 #else /* AFS_DEMAND_ATTACH_FS */
881     for (dp = DiskPartitionList; dp; dp = dp->next) {
882         if (strcmp(dp->name, name) == 0)
883             break;
884     }
885 #endif /* AFS_DEMAND_ATTACH_FS */
886     if (abortp)
887         assert(dp != NULL);
888     return dp;
889 }
890
891 struct DiskPartition64 *
892 VGetPartition(char *name, int abortp)
893 {
894     struct DiskPartition64 *retVal;
895     VOL_LOCK;
896     retVal = VGetPartition_r(name, abortp);
897     VOL_UNLOCK;
898     return retVal;
899 }
900
901 #ifdef AFS_NT40_ENV
902 void
903 VSetPartitionDiskUsage_r(register struct DiskPartition64 *dp)
904 {
905     ULARGE_INTEGER free_user, total, free_total;
906     int ufree, tot, tfree;
907
908     if (!GetDiskFreeSpaceEx
909         (VPartitionPath(dp), &free_user, &total, &free_total)) {
910         printf("Failed to get disk space info for %s, error = %d\n", dp->name,
911                GetLastError());
912         return;
913     }
914
915     /* Convert to 1K units. */
916     ufree = (int)Int64ShraMod32(free_user.QuadPart, 10);
917     tot = (int)Int64ShraMod32(total.QuadPart, 10);
918     tfree = (int)Int64ShraMod32(free_total.QuadPart, 10);
919
920     dp->minFree = tfree - ufree;        /* only used in VPrintDiskStats_r */
921     dp->totalUsable = tot;
922     dp->free = tfree;
923 }
924
925 #else
926 void
927 VSetPartitionDiskUsage_r(register struct DiskPartition64 *dp)
928 {
929     int bsize, code;
930     afs_int64 totalblks, free, used, availblks;
931     int reserved;
932 #ifdef afs_statvfs
933     struct afs_statvfs statbuf;
934 #else
935     struct afs_statfs statbuf;
936 #endif
937
938     if (dp->flags & PART_DONTUPDATE)
939         return;
940     /* Note:  we don't bother syncing because it's only an estimate, update
941      * is syncing every 30 seconds anyway, we only have to keep the disk
942      * approximately 10% from full--you just can't get the stuff in from
943      * the net fast enough to worry */
944 #ifdef afs_statvfs
945     code = afs_statvfs(dp->name, &statbuf);
946 #else
947     code = afs_statfs(dp->name, &statbuf);
948 #endif
949     if (code < 0) {
950         Log("statfs of %s failed in VSetPartitionDiskUsage (errno = %d)\n",
951             dp->name, errno);
952         return;
953     }
954     if (statbuf.f_blocks == -1) {       /* Undefined; skip stats.. */
955         Log("statfs of %s failed in VSetPartitionDiskUsage\n", dp->name);
956         return;
957     }
958     totalblks = statbuf.f_blocks;
959     free = statbuf.f_bfree;
960     reserved = free - statbuf.f_bavail;
961 #ifdef afs_statvfs
962     bsize = statbuf.f_frsize;
963 #else
964     bsize = statbuf.f_bsize;
965 #endif
966     availblks = totalblks - reserved;
967     dp->f_files = statbuf.f_files;      /* max # of files in partition */
968
969     /* Now free and totalblks are in fragment units, but we want them in
970      * 1K units.
971      */
972     if (bsize >= 1024) {
973         free *= (bsize / 1024);
974         totalblks *= (bsize / 1024);
975         availblks *= (bsize / 1024);
976         reserved *= (bsize / 1024);
977     } else {
978         free /= (1024 / bsize);
979         totalblks /= (1024 / bsize);
980         availblks /= (1024 / bsize);
981         reserved /= (1024 / bsize);
982     }
983     /* now compute remaining figures */
984     used = totalblks - free;
985
986     dp->minFree = reserved;     /* only used in VPrintDiskStats_r */
987     dp->totalUsable = availblks;
988     dp->free = availblks - used;        /* this is exactly f_bavail */
989 }
990 #endif /* AFS_NT40_ENV */
991
992 void
993 VSetPartitionDiskUsage(register struct DiskPartition64 *dp)
994 {
995     VOL_LOCK;
996     VSetPartitionDiskUsage_r(dp);
997     VOL_UNLOCK;
998 }
999
1000 void
1001 VResetDiskUsage_r(void)
1002 {
1003     struct DiskPartition64 *dp;
1004     for (dp = DiskPartitionList; dp; dp = dp->next) {
1005         VSetPartitionDiskUsage_r(dp);
1006 #ifndef AFS_PTHREAD_ENV
1007         IOMGR_Poll();
1008 #endif /* !AFS_PTHREAD_ENV */
1009     }
1010 }
1011
1012 void
1013 VResetDiskUsage(void)
1014 {
1015     VOL_LOCK;
1016     VResetDiskUsage_r();
1017     VOL_UNLOCK;
1018 }
1019
1020 void
1021 VAdjustDiskUsage_r(Error * ec, Volume * vp, afs_sfsize_t blocks,
1022                    afs_sfsize_t checkBlocks)
1023 {
1024     *ec = 0;
1025     /* why blocks instead of checkBlocks in the check below?  Otherwise, any check
1026      * for less than BlocksSpare would skip the error-checking path, and we
1027      * could grow existing files forever, not just for another BlocksSpare
1028      * blocks. */
1029     if (blocks > 0) {
1030 #ifdef  AFS_AIX32_ENV
1031         afs_int32 rem, minavail;
1032
1033         if ((rem = vp->partition->free - checkBlocks) < (minavail =
1034                                                          (vp->partition->
1035                                                           totalUsable *
1036                                                           aixlow_water) /
1037                                                          100))
1038 #else
1039         if (vp->partition->free - checkBlocks < 0)
1040 #endif
1041             *ec = VDISKFULL;
1042         else if (V_maxquota(vp)
1043                  && V_diskused(vp) + checkBlocks > V_maxquota(vp))
1044             *ec = VOVERQUOTA;
1045     }
1046     vp->partition->free -= blocks;
1047     V_diskused(vp) += blocks;
1048 }
1049
1050 void
1051 VAdjustDiskUsage(Error * ec, Volume * vp, afs_sfsize_t blocks,
1052                  afs_sfsize_t checkBlocks)
1053 {
1054     VOL_LOCK;
1055     VAdjustDiskUsage_r(ec, vp, blocks, checkBlocks);
1056     VOL_UNLOCK;
1057 }
1058
1059 int
1060 VDiskUsage_r(Volume * vp, afs_sfsize_t blocks)
1061 {
1062     if (blocks > 0) {
1063 #ifdef  AFS_AIX32_ENV
1064         afs_int32 rem, minavail;
1065
1066         if ((rem = vp->partition->free - blocks) < (minavail =
1067                                                     (vp->partition->
1068                                                      totalUsable *
1069                                                      aixlow_water) / 100))
1070 #else
1071         if (vp->partition->free - blocks < 0)
1072 #endif
1073             return (VDISKFULL);
1074     }
1075     vp->partition->free -= blocks;
1076     return 0;
1077 }
1078
1079 int
1080 VDiskUsage(Volume * vp, afs_sfsize_t blocks)
1081 {
1082     int retVal;
1083     VOL_LOCK;
1084     retVal = VDiskUsage_r(vp, blocks);
1085     VOL_UNLOCK;
1086     return retVal;
1087 }
1088
1089 void
1090 VPrintDiskStats_r(void)
1091 {
1092     struct DiskPartition64 *dp;
1093     for (dp = DiskPartitionList; dp; dp = dp->next) {
1094         if (dp->free < 0) {
1095             Log("Partition %s: %"AFS_INT64_FMT
1096                 " available 1K blocks (minfree=%"AFS_INT64_FMT"), "
1097                 "overallocated by %"AFS_INT64_FMT" blocks\n", dp->name,
1098                 dp->totalUsable, dp->minFree, -dp->free);
1099         } else {
1100             Log("Partition %s: %"AFS_INT64_FMT
1101                 " available 1K blocks (minfree=%"AFS_INT64_FMT"), "
1102                 "%"AFS_INT64_FMT" free blocks\n", dp->name,
1103                 dp->totalUsable, dp->minFree, dp->free);
1104         }
1105     }
1106 }
1107
1108 void
1109 VPrintDiskStats(void)
1110 {
1111     VOL_LOCK;
1112     VPrintDiskStats_r();
1113     VOL_UNLOCK;
1114 }
1115
1116 #ifdef AFS_NT40_ENV
1117 /* Need a separate lock file on NT, since NT only has mandatory file locks. */
1118 #define LOCKFILE "LOCKFILE"
1119 void
1120 VLockPartition_r(char *name)
1121 {
1122     struct DiskPartition64 *dp = VGetPartition_r(name, 0);
1123     OVERLAPPED lap;
1124
1125     if (!dp)
1126         return;
1127     if (dp->lock_fd == INVALID_FD) {
1128         char path[64];
1129         int rc;
1130         (void)sprintf(path, "%s\\%s", VPartitionPath(dp), LOCKFILE);
1131         dp->lock_fd =
1132             (FD_t)CreateFile(path, GENERIC_WRITE,
1133                             FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1134                             CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL);
1135         assert(dp->lock_fd != INVALID_FD);
1136
1137         memset(&lap, 0, sizeof(lap));
1138         rc = LockFileEx((HANDLE) dp->lock_fd, LOCKFILE_EXCLUSIVE_LOCK, 0, 1,
1139                         0, &lap);
1140         assert(rc);
1141     }
1142 }
1143
1144 void
1145 VUnlockPartition_r(char *name)
1146 {
1147     register struct DiskPartition64 *dp = VGetPartition_r(name, 0);
1148     OVERLAPPED lap;
1149
1150     if (!dp)
1151         return;                 /* no partition, will fail later */
1152     memset(&lap, 0, sizeof(lap));
1153
1154     UnlockFileEx((HANDLE) dp->lock_fd, 0, 1, 0, &lap);
1155     CloseHandle((HANDLE) dp->lock_fd);
1156     dp->lock_fd = INVALID_FD;
1157 }
1158 #else /* AFS_NT40_ENV */
1159
1160 #if defined(AFS_HPUX_ENV)
1161 #define BITS_PER_CHAR   (8)
1162 #define BITS(type)      (sizeof(type) * BITS_PER_CHAR)
1163
1164 #define LOCKRDONLY_OFFSET       ((PRIV_LOCKRDONLY - 1) / BITS(int))
1165 #endif /* defined(AFS_HPUX_ENV) */
1166
1167 void
1168 VLockPartition_r(char *name)
1169 {
1170     register struct DiskPartition64 *dp = VGetPartition_r(name, 0);
1171     char *partitionName;
1172     int retries, code;
1173     struct timeval pausing;
1174 #if defined(AFS_HPUX_ENV)
1175     int lockfRtn;
1176     struct privgrp_map privGrpList[PRIV_MAXGRPS];
1177     unsigned int *globalMask;
1178     int globalMaskIndex;
1179 #endif /* defined(AFS_HPUX_ENV) */
1180 #if defined(AFS_DARWIN_ENV)
1181     char lockfile[MAXPATHLEN];
1182 #endif /* defined(AFS_DARWIN_ENV) */
1183 #ifdef AFS_NAMEI_ENV
1184 #ifdef AFS_AIX42_ENV
1185     char LockFileName[MAXPATHLEN + 1];
1186
1187     sprintf((char *)&LockFileName, "%s/AFSINODE_FSLock", name);
1188     partitionName = (char *)&LockFileName;
1189 #endif
1190 #endif
1191
1192     if (!dp)
1193         return;                 /* no partition, will fail later */
1194     if (dp->lock_fd != -1)
1195         return;
1196
1197 #if    defined(AFS_SUN5_ENV) || defined(AFS_AIX41_ENV)
1198 #if !defined(AFS_AIX42_ENV) || !defined(AFS_NAMEI_ENV)
1199     partitionName = dp->devName;
1200 #endif
1201     code = O_RDWR;
1202 #elif defined(AFS_DARWIN_ENV)
1203     strlcpy((partitionName = lockfile), dp->name, sizeof(lockfile));
1204     strlcat(lockfile, "/.lock.afs", sizeof(lockfile));
1205     code = O_RDONLY | O_CREAT;
1206 #else
1207     partitionName = dp->name;
1208     code = O_RDONLY;
1209 #endif
1210
1211     for (retries = 25; retries; retries--) {
1212         if (code & O_CREAT)
1213             dp->lock_fd = afs_open(partitionName, code, 0644);
1214         else
1215             dp->lock_fd = afs_open(partitionName, code);
1216
1217         if (dp->lock_fd != -1)
1218             break;
1219         if (errno == ENOENT)
1220             code |= O_CREAT;
1221         pausing.tv_sec = 0;
1222         pausing.tv_usec = 500000;
1223         select(0, NULL, NULL, NULL, &pausing);
1224     }
1225     assert(retries != 0);
1226
1227 #if defined (AFS_HPUX_ENV)
1228
1229     assert(getprivgrp(privGrpList) == 0);
1230
1231     /*
1232      * In general, it will difficult and time-consuming ,if not impossible,
1233      * to try to find the privgroup to which this process belongs that has the
1234      * smallest membership, to minimise the security hole.  So, we use the privgrp
1235      * to which everybody belongs.
1236      */
1237     /* first, we have to find the global mask */
1238     for (globalMaskIndex = 0; globalMaskIndex < PRIV_MAXGRPS;
1239          globalMaskIndex++) {
1240         if (privGrpList[globalMaskIndex].priv_groupno == PRIV_GLOBAL) {
1241             globalMask =
1242                 &(privGrpList[globalMaskIndex].priv_mask[LOCKRDONLY_OFFSET]);
1243             break;
1244         }
1245     }
1246
1247     if (((*globalMask) & privmask(PRIV_LOCKRDONLY)) == 0) {
1248         /* allow everybody to set a lock on a read-only file descriptor */
1249         (*globalMask) |= privmask(PRIV_LOCKRDONLY);
1250         assert(setprivgrp(PRIV_GLOBAL, privGrpList[globalMaskIndex].priv_mask)
1251                == 0);
1252
1253         lockfRtn = lockf(dp->lock_fd, F_LOCK, 0);
1254
1255         /* remove the privilege granted to everybody to lock a read-only fd */
1256         (*globalMask) &= ~(privmask(PRIV_LOCKRDONLY));
1257         assert(setprivgrp(PRIV_GLOBAL, privGrpList[globalMaskIndex].priv_mask)
1258                == 0);
1259     } else {
1260         /* in this case, we should be able to do this with impunity, anyway */
1261         lockfRtn = lockf(dp->lock_fd, F_LOCK, 0);
1262     }
1263
1264     assert(lockfRtn != -1);
1265 #else
1266 #if defined(AFS_AIX_ENV) || defined(AFS_SUN5_ENV)
1267     assert(lockf(dp->lock_fd, F_LOCK, 0) != -1);
1268 #else
1269     assert(flock(dp->lock_fd, LOCK_EX) == 0);
1270 #endif /* defined(AFS_AIX_ENV) || defined(AFS_SUN5_ENV) */
1271 #endif
1272 }
1273
1274 void
1275 VUnlockPartition_r(char *name)
1276 {
1277     register struct DiskPartition64 *dp = VGetPartition_r(name, 0);
1278     if (!dp)
1279         return;                 /* no partition, will fail later */
1280     close(dp->lock_fd);
1281     dp->lock_fd = -1;
1282 }
1283
1284 #endif /* AFS_NT40_ENV */
1285
1286 void
1287 VLockPartition(char *name)
1288 {
1289     VOL_LOCK;
1290     VLockPartition_r(name);
1291     VOL_UNLOCK;
1292 }
1293
1294 void
1295 VUnlockPartition(char *name)
1296 {
1297     VOL_LOCK;
1298     VUnlockPartition_r(name);
1299     VOL_UNLOCK;
1300 }
1301
1302 #ifdef AFS_DEMAND_ATTACH_FS
1303
1304 /* new-style partition locks; these are only to have some mutual exclusion
1305  * between the VGC scanner and volume utilies creating/altering vol headers
1306  */
1307
1308 /**
1309  * lock a partition's vol headers.
1310  *
1311  * @param[in] dp       the partition to lock
1312  * @param[in] locktype READ_LOCK or WRITE_LOCK
1313  *
1314  * @return operation status
1315  *  @retval 0 success
1316  */
1317 int
1318 VPartHeaderLock(struct DiskPartition64 *dp, int locktype)
1319 {
1320     int code;
1321
1322     /* block on acquiring the lock */
1323     int nonblock = 0;
1324
1325     code = VGetDiskLock(&dp->headerLock, locktype, nonblock);
1326     if (code) {
1327         Log("VPartHeaderLock: error %d locking partititon %s\n", code,
1328             VPartitionPath(dp));
1329     }
1330     return code;
1331 }
1332
1333 /**
1334  * unlock a partition's vol headers.
1335  *
1336  * @param[in] dp       the partition to unlock
1337  * @param[in] locktype READ_LOCK or WRITE_LOCK
1338  */
1339 void
1340 VPartHeaderUnlock(struct DiskPartition64 *dp, int locktype)
1341 {
1342     VReleaseDiskLock(&dp->headerLock, locktype);
1343 }
1344
1345 /* XXX not sure this will work on AFS_NT40_ENV
1346  * needs to be tested!
1347  */
1348
1349 /**
1350  * lookup a disk partition object by its index number.
1351  *
1352  * @param[in] id      partition index number
1353  * @param[in] abortp  see abortp usage note below
1354  *
1355  * @return disk partition object
1356  *   @retval NULL no such disk partition
1357  *
1358  * @note when abortp is non-zero, lookups which would return
1359  *       NULL will result in an assertion failure
1360  *
1361  * @pre VOL_LOCK must be held
1362  *
1363  * @internal volume package internal use only
1364  */
1365
1366 struct DiskPartition64 * 
1367 VGetPartitionById_r(afs_int32 id, int abortp)
1368 {
1369     struct DiskPartition64 *dp = NULL;
1370
1371     if ((id >= 0) && (id <= VOLMAXPARTS)) {
1372         dp = DiskPartitionTable[id];
1373     }
1374
1375     if (abortp) {
1376         assert(dp != NULL);
1377     }
1378     return dp;
1379 }
1380
1381 /**
1382  * lookup a disk partition object by its index number.
1383  *
1384  * @param[in] id      partition index number
1385  * @param[in] abortp  see abortp usage note below
1386  *
1387  * @return disk partition object
1388  *   @retval NULL no such disk partition
1389  *
1390  * @note when abortp is non-zero, lookups which would return
1391  *       NULL will result in an assertion failure
1392  */
1393
1394 struct DiskPartition64 *
1395 VGetPartitionById(afs_int32 id, int abortp)
1396 {
1397     struct DiskPartition64 * dp;
1398
1399     VOL_LOCK;
1400     dp = VGetPartitionById_r(id, abortp);
1401     VOL_UNLOCK;
1402
1403     return dp;
1404 }
1405
1406 static struct DiskPartition64 * 
1407 VLookupPartition_r(char * path)
1408 {
1409     afs_int32 id = volutil_GetPartitionID(path);
1410
1411     if (id < 0 || id > VOLMAXPARTS)
1412         return NULL;
1413
1414     return DiskPartitionTable[id];
1415 }
1416
1417 static void 
1418 AddPartitionToTable_r(struct DiskPartition64 *dp)
1419 {
1420     assert(dp->index >= 0 && dp->index <= VOLMAXPARTS);
1421     DiskPartitionTable[dp->index] = dp;
1422 }
1423
1424 #if 0
1425 static void 
1426 DeletePartitionFromTable_r(struct DiskPartition64 *dp)
1427 {
1428     assert(dp->index >= 0 && dp->index <= VOLMAXPARTS);
1429     DiskPartitionTable[dp->index] = NULL;
1430 }
1431 #endif
1432 #endif /* AFS_DEMAND_ATTACH_FS */