dafs-20060317
[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 #ifdef AFS_NT40_ENV
29 #include <windows.h>
30 #include <winbase.h>
31 #include <winioctl.h>
32 #else
33 #include <sys/param.h>
34 #include <sys/types.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 #ifdef HAVE_STRING_H
111 #include <string.h>
112 #else
113 #ifdef HAVE_STRINGS_H
114 #include <strings.h>
115 #endif
116 #endif
117
118 #include <rx/xdr.h>
119 #include <afs/afsint.h>
120 #include "nfs.h"
121 #include <afs/errors.h>
122 #include "lock.h"
123 #include "lwp.h"
124 #include <afs/afssyscalls.h>
125 #include "ihandle.h"
126 #ifdef AFS_NAMEI_ENV
127 #ifdef AFS_NT40_ENV
128 #include "ntops.h"
129 #else
130 #include "namei_ops.h"
131 #include <dirent.h>
132 #endif /* AFS_NT40_ENV */
133 #endif /* AFS_NAMEI_ENV */
134 #include "vnode.h"
135 #include "volume.h"
136 #include "partition.h"
137 #ifdef AFS_PTHREAD_ENV
138 #include <assert.h>
139 #else /* AFS_PTHREAD_ENV */
140 #include <afs/assert.h>
141 #endif /* AFS_PTHREAD_ENV */
142
143 #if defined(AFS_HPUX_ENV)
144 #include <sys/types.h>
145 #include <sys/privgrp.h>
146 #endif /* defined(AFS_HPUX_ENV) */
147
148 #ifdef AFS_AIX42_ENV
149 #include <jfs/filsys.h>
150 #endif
151
152 #ifdef O_LARGEFILE
153
154 #define afs_stat        stat64
155 #define afs_open        open64
156 #define afs_fopen       fopen64
157 #ifndef AFS_NT40_ENV
158 #if AFS_HAVE_STATVFS64
159 # define afs_statvfs    statvfs64
160 #else
161 # if AFS_HAVE_STATFS64
162 #  define afs_statfs    statfs64
163 #else
164 #  if AFS_HAVE_STATVFS
165 #   define afs_statvfs  statvfs
166 #  else
167 #   define afs_statfs   statfs
168 #  endif /* !AFS_HAVE_STATVFS */
169 # endif /* !AFS_HAVE_STATFS64 */
170 #endif /* !AFS_HAVE_STATVFS64 */
171 #endif /* !AFS_NT40_ENV */
172
173 #else /* !O_LARGEFILE */
174
175 #define afs_stat        stat
176 #define afs_open        open
177 #define afs_fopen       fopen
178 #ifndef AFS_NT40_ENV
179 #if AFS_HAVE_STATVFS
180 #define afs_statvfs     statvfs
181 #else /* !AFS_HAVE_STATVFS */
182 #define afs_statfs      statfs
183 #endif /* !AFS_HAVE_STATVFS */
184 #endif /* !AFS_NT40_ENV */
185
186 #endif /* !O_LARGEFILE */
187
188 /*@printflike@*/ extern void Log(const char *format, ...);
189
190 int aixlow_water = 8;           /* default 8% */
191 struct DiskPartition *DiskPartitionList;
192
193 #ifdef AFS_DEMAND_ATTACH_FS
194 static struct DiskPartition *DiskPartitionTable[VOLMAXPARTS+1];
195
196 static struct DiskPartition * VLookupPartition_r(char * path);
197 static void AddPartitionToTable_r(struct DiskPartition *);
198 static void DeletePartitionFromTable_r(struct DiskPartition *);
199 #endif /* AFS_DEMAND_ATTACH_FS */
200
201 #ifdef AFS_SGI_XFS_IOPS_ENV
202 /* Verify that the on disk XFS inodes on the partition are large enough to
203  * hold the AFS attribute. Returns -1 if the attribute can't be set or is
204  * too small to fit in the inode. Returns 0 if the attribute does fit in
205  * the XFS inode.
206  */
207 #include <afs/xfsattrs.h>
208 static int
209 VerifyXFSInodeSize(char *part, char *fstype)
210 {
211     afs_xfs_attr_t junk;
212     int length = SIZEOF_XFS_ATTR_T;
213     int fd = 0;
214     int code = -1;
215     struct fsxattr fsx;
216
217     if (strcmp("xfs", fstype))
218         return 0;
219
220     if (attr_set(part, AFS_XFS_ATTR, &junk, length, ATTR_ROOT) == 0) {
221         if (((fd = open(part, O_RDONLY, 0)) != -1)
222             && (fcntl(fd, F_FSGETXATTRA, &fsx) == 0)) {
223
224             if (fsx.fsx_nextents) {
225                 Log("Partition %s: XFS inodes too small, exiting.\n", part);
226                 Log("Run xfs_size_check utility and remake partitions.\n");
227             } else
228                 code = 0;
229         }
230
231         if (fd > 0)
232             close(fd);
233         (void)attr_remove(part, AFS_XFS_ATTR, ATTR_ROOT);
234     }
235     return code;
236 }
237 #endif /* AFS_SGI_XFS_IOPS_ENV */
238
239 int
240 VInitPartitionPackage(void)
241 {
242 #ifdef AFS_DEMAND_ATTACH_ENV
243     memset(&DiskPartitionTable, 0, sizeof(DiskPartitionTable));
244 #endif /* AFS_DEMAND_ATTACH_ENV */
245     return 0;
246 }
247
248 static void
249 VInitPartition_r(char *path, char *devname, Device dev)
250 {
251     struct DiskPartition *dp, *op;
252     dp = (struct DiskPartition *)malloc(sizeof(struct DiskPartition));
253     /* Add it to the end, to preserve order when we print statistics */
254     for (op = DiskPartitionList; op; op = op->next) {
255         if (!op->next)
256             break;
257     }
258     if (op)
259         op->next = dp;
260     else
261         DiskPartitionList = dp;
262     dp->next = 0;
263     dp->name = (char *)malloc(strlen(path) + 1);
264     strncpy(dp->name, path, strlen(path) + 1);
265     dp->index = volutil_GetPartitionID(path);
266 #if defined(AFS_NAMEI_ENV) && !defined(AFS_NT40_ENV)
267     /* Create a lockfile for the partition, of the form /vicepa/Lock/vicepa */
268     dp->devName = (char *)malloc(2 * strlen(path) + 6);
269     strcpy(dp->devName, path);
270     strcat(dp->devName, "/");
271     strcat(dp->devName, "Lock");
272     mkdir(dp->devName, 0700);
273     strcat(dp->devName, path);
274     close(afs_open(dp->devName, O_RDWR | O_CREAT, 0600));
275     dp->device = dp->index;
276 #else
277     dp->devName = (char *)malloc(strlen(devname) + 1);
278     strncpy(dp->devName, devname, strlen(devname) + 1);
279     dp->device = dev;
280 #endif
281     dp->lock_fd = -1;
282     dp->flags = 0;
283     dp->f_files = 1;            /* just a default value */
284 #if defined(AFS_NAMEI_ENV) && !defined(AFS_NT40_ENV)
285     if (programType == fileServer)
286         (void)namei_ViceREADME(VPartitionPath(dp));
287 #endif
288     VSetPartitionDiskUsage_r(dp);
289 #ifdef AFS_DEMAND_ATTACH_FS
290     AddPartitionToTable_r(dp);
291     queue_Init(&dp->vol_list);
292     assert(pthread_cond_init(&dp->vol_list.cv, NULL) == 0);
293 #endif /* AFS_DEMAND_ATTACH_FS */
294 }
295
296 static void
297 VInitPartition(char *path, char *devname, Device dev)
298 {
299     VOL_LOCK;
300     VInitPartition_r(path, devname, dev);
301     VOL_UNLOCK;
302 }
303
304 #ifndef AFS_NT40_ENV
305 /* VAttachPartitions() finds the vice partitions on this server. Calls
306  * VCheckPartition() to do some basic checks on the partition. If the partition
307  * is a valid vice partition, VCheckPartition will add it to the DiskPartition
308  * list.
309  * Returns the number of errors returned by VCheckPartition. An error in
310  * VCheckPartition means that partition is a valid vice partition but the
311  * fileserver should not start because of the error found on that partition.
312  *
313  * AFS_NAMEI_ENV
314  * No specific user space file system checks, since we don't know what
315  * is being used for vice partitions.
316  *
317  * Use partition name as devname.
318  */
319 int
320 VCheckPartition(char *part, char *devname)
321 {
322     struct afs_stat status;
323 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_NT40_ENV)
324     char AFSIDatPath[MAXPATHLEN];
325 #endif
326
327     /* Only keep track of "/vicepx" partitions since it can get hairy
328      * when NFS mounts are involved.. */
329     if (strncmp(part, VICE_PARTITION_PREFIX, VICE_PREFIX_SIZE)) {
330         return 0;
331     }
332     if (afs_stat(part, &status) < 0) {
333         Log("VInitVnodes: Couldn't find file system %s; ignored\n", part);
334         return 0;
335     }
336 #ifndef AFS_AIX32_ENV
337     if (programType == fileServer) {
338         char salvpath[MAXPATHLEN];
339         strcpy(salvpath, part);
340         strcat(salvpath, "/FORCESALVAGE");
341         if (afs_stat(salvpath, &status) == 0) {
342             Log("VInitVnodes: Found %s; aborting\n", salvpath);
343             return -1;
344         }
345     }
346 #endif
347
348 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_NT40_ENV)
349     strcpy(AFSIDatPath, part);
350     strcat(AFSIDatPath, "/AFSIDat");
351 #ifdef AFS_NAMEI_ENV
352     if (afs_stat(AFSIDatPath, &status) < 0) {
353         DIR *dirp;
354         struct dirent *dp;
355
356         dirp = opendir(part);
357         assert(dirp);
358         while (dp = readdir(dirp)) {
359             if (dp->d_name[0] == 'V') {
360                 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);
361                 closedir(dirp);
362                 return -1;
363             }
364         }
365         closedir(dirp);
366     }
367 #else /* AFS_NAMEI_ENV */
368     if (afs_stat(AFSIDatPath, &status) == 0) {
369         Log("This program is compiled without AFS_NAMEI_ENV, but partition %s seems to contain volumes which use the namei-interface; aborting\n", part);
370         return -1;
371     }
372
373 #ifdef AFS_SGI_XFS_IOPS_ENV
374     if (VerifyXFSInodeSize(part, status.st_fstype) < 0)
375         return -1;
376 #endif
377 #endif /* AFS_NAMEI_ENV */
378 #endif /* !AFS_LINUX20_ENV && !AFS_NT40_ENV */
379
380 #if defined(AFS_DUX40_ENV) && !defined(AFS_NAMEI_ENV)
381     if (status.st_ino != ROOTINO) {
382         Log("%s is not a mounted file system; ignored.\n", part);
383         return 0;
384     }
385 #endif
386
387     VInitPartition(part, devname, status.st_dev);
388
389     return 0;
390 }
391
392 /* VIsAlwaysAttach() checks whether a /vicepX directory should always be
393  * attached (return value 1), or only attached when it is a separately
394  * mounted partition (return value 0).  For non-NAMEI environments, it
395  * always returns 0.
396  */
397 static int
398 VIsAlwaysAttach(char *part)
399 {
400 #ifdef AFS_NAMEI_ENV
401     struct afs_stat st;
402     char checkfile[256];
403     int ret;
404
405     if (strncmp(part, VICE_PARTITION_PREFIX, VICE_PREFIX_SIZE))
406         return 0;
407
408     strncpy(checkfile, part, 100);
409     strcat(checkfile, "/");
410     strcat(checkfile, VICE_ALWAYSATTACH_FILE);
411
412     ret = afs_stat(checkfile, &st);
413     return (ret < 0) ? 0 : 1;
414 #else /* AFS_NAMEI_ENV */
415     return 0;
416 #endif /* AFS_NAMEI_ENV */
417 }
418
419 /* VAttachPartitions2() looks for and attaches /vicepX partitions
420  * where a special file (VICE_ALWAYSATTACH_FILE) exists.  This is
421  * used to attach /vicepX directories which aren't on dedicated
422  * partitions, in the NAMEI fileserver.
423  */
424 void
425 VAttachPartitions2()
426 {
427 #ifdef AFS_NAMEI_ENV
428     DIR *dirp;
429     struct dirent *de;
430     char pname[32];
431
432     dirp = opendir("/");
433     while ((de = readdir(dirp))) {
434         strcpy(pname, "/");
435         strncat(pname, de->d_name, 20);
436         pname[sizeof(pname) - 1] = '\0';
437
438         /* Only keep track of "/vicepx" partitions since automounter
439          * may hose us */
440         if (VIsAlwaysAttach(pname))
441             VCheckPartition(pname, "");
442     }
443     closedir(dirp);
444 #endif /* AFS_NAMEI_ENV */
445 }
446 #endif /* AFS_NT40_ENV */
447
448 #ifdef AFS_SUN5_ENV
449 int
450 VAttachPartitions(void)
451 {
452     int errors = 0;
453     struct mnttab mnt;
454     FILE *mntfile;
455
456     if (!(mntfile = afs_fopen(MNTTAB, "r"))) {
457         Log("Can't open %s\n", MNTTAB);
458         perror(MNTTAB);
459         exit(-1);
460     }
461     while (!getmntent(mntfile, &mnt)) {
462         /* Ignore non ufs or non read/write partitions */
463         if ((strcmp(mnt.mnt_fstype, "ufs") != 0)
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 */