large-partition-support-20080305
[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 DiskPartition64 *DiskPartitionList;
185
186 #ifdef AFS_DEMAND_ATTACH_FS
187 static struct DiskPartition64 *DiskPartitionTable[VOLMAXPARTS+1];
188
189 static struct DiskPartition64 * VLookupPartition_r(char * path);
190 static void AddPartitionToTable_r(struct DiskPartition64 *);
191 static void DeletePartitionFromTable_r(struct DiskPartition64 *);
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 DiskPartition64 *dp, *op;
245     dp = (struct DiskPartition64 *)malloc(sizeof(struct DiskPartition64));
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 DiskPartition64 *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 DiskPartition64 *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 DiskPartition64 *
848 VGetPartition_r(char *name, int abortp)
849 {
850     register struct DiskPartition64 *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 DiskPartition64 *
865 VGetPartition(char *name, int abortp)
866 {
867     struct DiskPartition64 *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 DiskPartition64 *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 DiskPartition64 *dp)
901 {
902     int fd, bsize, code;
903     afs_int64 totalblks, free, used, availblks;
904     int reserved;
905 #ifdef afs_statvfs
906     struct afs_statvfs statbuf;
907 #else
908     struct afs_statfs statbuf;
909 #endif
910
911     if (dp->flags & PART_DONTUPDATE)
912         return;
913     /* Note:  we don't bother syncing because it's only an estimate, update
914      * is syncing every 30 seconds anyway, we only have to keep the disk
915      * approximately 10% from full--you just can't get the stuff in from
916      * the net fast enough to worry */
917 #ifdef afs_statvfs
918     code = afs_statvfs(dp->name, &statbuf);
919 #else
920     code = afs_statfs(dp->name, &statbuf);
921 #endif
922     if (code < 0) {
923         Log("statfs of %s failed in VSetPartitionDiskUsage (errno = %d)\n",
924             dp->name, errno);
925         return;
926     }
927     if (statbuf.f_blocks == -1) {       /* Undefined; skip stats.. */
928         Log("statfs of %s failed in VSetPartitionDiskUsage\n", dp->name);
929         return;
930     }
931     totalblks = statbuf.f_blocks;
932     free = statbuf.f_bfree;
933     reserved = free - statbuf.f_bavail;
934 #ifdef afs_statvfs
935     bsize = statbuf.f_frsize;
936 #else
937     bsize = statbuf.f_bsize;
938 #endif
939     availblks = totalblks - reserved;
940     dp->f_files = statbuf.f_files;      /* max # of files in partition */
941
942     /* Now free and totalblks are in fragment units, but we want them in
943      * 1K units.
944      */
945     if (bsize >= 1024) {
946         free *= (bsize / 1024);
947         totalblks *= (bsize / 1024);
948         availblks *= (bsize / 1024);
949         reserved *= (bsize / 1024);
950     } else {
951         free /= (1024 / bsize);
952         totalblks /= (1024 / bsize);
953         availblks /= (1024 / bsize);
954         reserved /= (1024 / bsize);
955     }
956     /* now compute remaining figures */
957     used = totalblks - free;
958
959     dp->minFree = reserved;     /* only used in VPrintDiskStats_r */
960     dp->totalUsable = availblks;
961     dp->free = availblks - used;        /* this is exactly f_bavail */
962 }
963 #endif /* AFS_NT40_ENV */
964
965 void
966 VSetPartitionDiskUsage(register struct DiskPartition64 *dp)
967 {
968     VOL_LOCK;
969     VSetPartitionDiskUsage_r(dp);
970     VOL_UNLOCK;
971 }
972
973 void
974 VResetDiskUsage_r(void)
975 {
976     struct DiskPartition64 *dp;
977     for (dp = DiskPartitionList; dp; dp = dp->next) {
978         VSetPartitionDiskUsage_r(dp);
979 #ifndef AFS_PTHREAD_ENV
980         IOMGR_Poll();
981 #endif /* !AFS_PTHREAD_ENV */
982     }
983 }
984
985 void
986 VResetDiskUsage(void)
987 {
988     VOL_LOCK;
989     VResetDiskUsage_r();
990     VOL_UNLOCK;
991 }
992
993 void
994 VAdjustDiskUsage_r(Error * ec, Volume * vp, afs_sfsize_t blocks,
995                    afs_sfsize_t checkBlocks)
996 {
997     *ec = 0;
998     /* why blocks instead of checkBlocks in the check below?  Otherwise, any check
999      * for less than BlocksSpare would skip the error-checking path, and we
1000      * could grow existing files forever, not just for another BlocksSpare
1001      * blocks. */
1002     if (blocks > 0) {
1003 #ifdef  AFS_AIX32_ENV
1004         afs_int32 rem, minavail;
1005
1006         if ((rem = vp->partition->free - checkBlocks) < (minavail =
1007                                                          (vp->partition->
1008                                                           totalUsable *
1009                                                           aixlow_water) /
1010                                                          100))
1011 #else
1012         if (vp->partition->free - checkBlocks < 0)
1013 #endif
1014             *ec = VDISKFULL;
1015         else if (V_maxquota(vp)
1016                  && V_diskused(vp) + checkBlocks > V_maxquota(vp))
1017             *ec = VOVERQUOTA;
1018     }
1019     vp->partition->free -= blocks;
1020     V_diskused(vp) += blocks;
1021 }
1022
1023 void
1024 VAdjustDiskUsage(Error * ec, Volume * vp, afs_sfsize_t blocks,
1025                  afs_sfsize_t checkBlocks)
1026 {
1027     VOL_LOCK;
1028     VAdjustDiskUsage_r(ec, vp, blocks, checkBlocks);
1029     VOL_UNLOCK;
1030 }
1031
1032 int
1033 VDiskUsage_r(Volume * vp, afs_sfsize_t blocks)
1034 {
1035     if (blocks > 0) {
1036 #ifdef  AFS_AIX32_ENV
1037         afs_int32 rem, minavail;
1038
1039         if ((rem = vp->partition->free - blocks) < (minavail =
1040                                                     (vp->partition->
1041                                                      totalUsable *
1042                                                      aixlow_water) / 100))
1043 #else
1044         if (vp->partition->free - blocks < 0)
1045 #endif
1046             return (VDISKFULL);
1047     }
1048     vp->partition->free -= blocks;
1049     return 0;
1050 }
1051
1052 int
1053 VDiskUsage(Volume * vp, afs_sfsize_t blocks)
1054 {
1055     int retVal;
1056     VOL_LOCK;
1057     retVal = VDiskUsage_r(vp, blocks);
1058     VOL_UNLOCK;
1059     return retVal;
1060 }
1061
1062 void
1063 VPrintDiskStats_r(void)
1064 {
1065     struct DiskPartition64 *dp;
1066     for (dp = DiskPartitionList; dp; dp = dp->next) {
1067         Log("Partition %s: %d available 1K blocks (minfree=%d), ", dp->name,
1068             dp->totalUsable, dp->minFree);
1069         if (dp->free < 0)
1070             Log("overallocated by %d blocks\n", -dp->free);
1071         else
1072             Log("%d free blocks\n", dp->free);
1073     }
1074 }
1075
1076 void
1077 VPrintDiskStats(void)
1078 {
1079     VOL_LOCK;
1080     VPrintDiskStats_r();
1081     VOL_UNLOCK;
1082 }
1083
1084 #ifdef AFS_NT40_ENV
1085 /* Need a separate lock file on NT, since NT only has mandatory file locks. */
1086 #define LOCKFILE "LOCKFILE"
1087 void
1088 VLockPartition_r(char *name)
1089 {
1090     struct DiskPartition64 *dp = VGetPartition_r(name, 0);
1091     OVERLAPPED lap;
1092
1093     if (!dp)
1094         return;
1095     if (dp->lock_fd == -1) {
1096         char path[64];
1097         int rc;
1098         (void)sprintf(path, "%s\\%s", VPartitionPath(dp), LOCKFILE);
1099         dp->lock_fd =
1100             (int)CreateFile(path, GENERIC_WRITE,
1101                             FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1102                             CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL);
1103         assert(dp->lock_fd != (int)INVALID_HANDLE_VALUE);
1104
1105         memset((char *)&lap, 0, sizeof(lap));
1106         rc = LockFileEx((HANDLE) dp->lock_fd, LOCKFILE_EXCLUSIVE_LOCK, 0, 1,
1107                         0, &lap);
1108         assert(rc);
1109     }
1110 }
1111
1112 void
1113 VUnlockPartition_r(char *name)
1114 {
1115     register struct DiskPartition64 *dp = VGetPartition_r(name, 0);
1116     OVERLAPPED lap;
1117
1118     if (!dp)
1119         return;                 /* no partition, will fail later */
1120     memset((char *)&lap, 0, sizeof(lap));
1121
1122     UnlockFileEx((HANDLE) dp->lock_fd, 0, 1, 0, &lap);
1123     CloseHandle((HANDLE) dp->lock_fd);
1124     dp->lock_fd = -1;
1125 }
1126 #else /* AFS_NT40_ENV */
1127
1128 #if defined(AFS_HPUX_ENV)
1129 #define BITS_PER_CHAR   (8)
1130 #define BITS(type)      (sizeof(type) * BITS_PER_CHAR)
1131
1132 #define LOCKRDONLY_OFFSET       ((PRIV_LOCKRDONLY - 1) / BITS(int))
1133 #endif /* defined(AFS_HPUX_ENV) */
1134
1135 void
1136 VLockPartition_r(char *name)
1137 {
1138     register struct DiskPartition64 *dp = VGetPartition_r(name, 0);
1139     char *partitionName;
1140     int retries, code;
1141     struct timeval pausing;
1142 #if defined(AFS_HPUX_ENV)
1143     int lockfRtn;
1144     struct privgrp_map privGrpList[PRIV_MAXGRPS];
1145     unsigned int *globalMask;
1146     int globalMaskIndex;
1147 #endif /* defined(AFS_HPUX_ENV) */
1148 #if defined(AFS_DARWIN_ENV)
1149     char lockfile[MAXPATHLEN];
1150 #endif /* defined(AFS_DARWIN_ENV) */
1151 #ifdef AFS_NAMEI_ENV
1152 #ifdef AFS_AIX42_ENV
1153     char LockFileName[MAXPATHLEN + 1];
1154
1155     sprintf((char *)&LockFileName, "%s/AFSINODE_FSLock", name);
1156     partitionName = (char *)&LockFileName;
1157 #endif
1158 #endif
1159
1160     if (!dp)
1161         return;                 /* no partition, will fail later */
1162     if (dp->lock_fd != -1)
1163         return;
1164
1165 #if    defined(AFS_SUN5_ENV) || defined(AFS_AIX41_ENV)
1166 #if !defined(AFS_AIX42_ENV) || !defined(AFS_NAMEI_ENV)
1167     partitionName = dp->devName;
1168 #endif
1169     code = O_RDWR;
1170 #elif defined(AFS_DARWIN_ENV)
1171     strlcpy((partitionName = lockfile), dp->name, sizeof(lockfile));
1172     strlcat(lockfile, "/.lock.afs", sizeof(lockfile));
1173     code = O_RDONLY | O_CREAT;
1174 #else
1175     partitionName = dp->name;
1176     code = O_RDONLY;
1177 #endif
1178
1179     for (retries = 25; retries; retries--) {
1180         dp->lock_fd = afs_open(partitionName, code);
1181         if (dp->lock_fd != -1)
1182             break;
1183         if (errno == ENOENT)
1184             code |= O_CREAT;
1185         pausing.tv_sec = 0;
1186         pausing.tv_usec = 500000;
1187         select(0, NULL, NULL, NULL, &pausing);
1188     }
1189     assert(retries != 0);
1190
1191 #if defined (AFS_HPUX_ENV)
1192
1193     assert(getprivgrp(privGrpList) == 0);
1194
1195     /*
1196      * In general, it will difficult and time-consuming ,if not impossible,
1197      * to try to find the privgroup to which this process belongs that has the
1198      * smallest membership, to minimise the security hole.  So, we use the privgrp
1199      * to which everybody belongs.
1200      */
1201     /* first, we have to find the global mask */
1202     for (globalMaskIndex = 0; globalMaskIndex < PRIV_MAXGRPS;
1203          globalMaskIndex++) {
1204         if (privGrpList[globalMaskIndex].priv_groupno == PRIV_GLOBAL) {
1205             globalMask =
1206                 &(privGrpList[globalMaskIndex].priv_mask[LOCKRDONLY_OFFSET]);
1207             break;
1208         }
1209     }
1210
1211     if (((*globalMask) & privmask(PRIV_LOCKRDONLY)) == 0) {
1212         /* allow everybody to set a lock on a read-only file descriptor */
1213         (*globalMask) |= privmask(PRIV_LOCKRDONLY);
1214         assert(setprivgrp(PRIV_GLOBAL, privGrpList[globalMaskIndex].priv_mask)
1215                == 0);
1216
1217         lockfRtn = lockf(dp->lock_fd, F_LOCK, 0);
1218
1219         /* remove the privilege granted to everybody to lock a read-only fd */
1220         (*globalMask) &= ~(privmask(PRIV_LOCKRDONLY));
1221         assert(setprivgrp(PRIV_GLOBAL, privGrpList[globalMaskIndex].priv_mask)
1222                == 0);
1223     } else {
1224         /* in this case, we should be able to do this with impunity, anyway */
1225         lockfRtn = lockf(dp->lock_fd, F_LOCK, 0);
1226     }
1227
1228     assert(lockfRtn != -1);
1229 #else
1230 #if defined(AFS_AIX_ENV) || defined(AFS_SUN5_ENV)
1231     assert(lockf(dp->lock_fd, F_LOCK, 0) != -1);
1232 #else
1233     assert(flock(dp->lock_fd, LOCK_EX) == 0);
1234 #endif /* defined(AFS_AIX_ENV) || defined(AFS_SUN5_ENV) */
1235 #endif
1236 }
1237
1238 void
1239 VUnlockPartition_r(char *name)
1240 {
1241     register struct DiskPartition64 *dp = VGetPartition_r(name, 0);
1242     if (!dp)
1243         return;                 /* no partition, will fail later */
1244     close(dp->lock_fd);
1245     dp->lock_fd = -1;
1246 }
1247
1248 #endif /* AFS_NT40_ENV */
1249
1250 void
1251 VLockPartition(char *name)
1252 {
1253     VOL_LOCK;
1254     VLockPartition_r(name);
1255     VOL_UNLOCK;
1256 }
1257
1258 void
1259 VUnlockPartition(char *name)
1260 {
1261     VOL_LOCK;
1262     VUnlockPartition_r(name);
1263     VOL_UNLOCK;
1264 }
1265
1266 #ifdef AFS_DEMAND_ATTACH_FS
1267 /* XXX not sure this will work on AFS_NT40_ENV
1268  * needs to be tested!
1269  */
1270 struct DiskPartition64 * 
1271 VGetPartitionById_r(afs_int32 id, int abortp)
1272 {
1273     struct DiskPartition64 *dp = NULL;
1274
1275     if ((id >= 0) && (id <= VOLMAXPARTS)) {
1276         dp = DiskPartitionTable[id];
1277     }
1278
1279     if (abortp) {
1280         assert(dp != NULL);
1281     }
1282     return dp;
1283 }
1284
1285 struct DiskPartition64 *
1286 VGetPartitionById(afs_int32 id, int abortp)
1287 {
1288     struct Diskpartition64 * dp;
1289
1290     VOL_LOCK;
1291     dp = VGetPartitionById_r(id, abortp);
1292     VOL_UNLOCK;
1293
1294     return dp;
1295 }
1296
1297 static struct DiskPartition64 * 
1298 VLookupPartition_r(char * path)
1299 {
1300     afs_int32 id = volutil_GetPartitionID(path);
1301
1302     if (id < 0 || id > VOLMAXPARTS)
1303         return NULL;
1304
1305     return DiskPartitionTable[id];
1306 }
1307
1308 static void 
1309 AddPartitionToTable_r(struct DiskPartition64 *dp)
1310 {
1311     assert(dp->index >= 0 && dp->index <= VOLMAXPARTS);
1312     DiskPartitionTable[dp->index] = dp;
1313 }
1314
1315 static void 
1316 DeletePartitionFromTable_r(struct DiskPartition64 *dp)
1317 {
1318     assert(dp->index >= 0 && dp->index <= VOLMAXPARTS);
1319     DiskPartitionTable[dp->index] = NULL;
1320 }
1321 #endif /* AFS_DEMAND_ATTACH_FS */