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