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