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