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