Xref: utzoo comp.protocols.nfs:761 comp.unix.i386:3477 Path: utzoo!utgpu!jarvis.csri.toronto.edu!clyde.concordia.ca!uunet!auspex!guy From: guy@auspex.auspex.com (Guy Harris) Newsgroups: comp.protocols.nfs,comp.unix.i386 Subject: Re: statbug.c: Spot a BUG with NFS stat() - please try this. Esp 386/ix! Message-ID: <3016@auspex.auspex.com> Date: 10 Mar 90 22:51:42 GMT References: <638@hades.OZ> Followup-To: comp.protocols.nfs Organization: Auspex Systems, Santa Clara Lines: 141 > On my system the st_dev number returned is negative; clearly a load of > garbage. There is no reason why a negative "st_dev" is necessarily "a load of garbage". For an extreme example, consider a system with the (currently-standard) 8 bits of major and 8 bits of minor device number, and with 129 different block device drivers.... Many UNIX NFS implementations choose negative "st_dev" values for remotely-mounted file systems (NFS or RFS), simply to avoid colliding with values for local file systems. The problem with "ed" is probably that "ustat()", while working *perfectly correctly* with those allegedly-"garbage" "st_dev" values, may not be returning proper data for an NFS-mounted file system. Another problem may be that it can't cope with "st_dev" values for file systems other than local ones, or for NFS file systems in particular. It certainly *is* possible to have "st_dev" return negative values for NFS-mounted file systems *and* to have "ustat()" work just fine with NFS-mounted file systems - SunOS has done so since SunOS 3.2. The following test program can be used to see how well "ustat()" works. It takes one argument, which should be the path name of a file (any file or directory) on the file system on which you want to test this, and prints out the "ustat" information for that file system. To check its answers, do a "df" on the file system in question and compare the results. NOTE: beware of the units that "ustat" may be using. UNIX software tends to assume that the "f_tfree" figure is in units of 512-byte blocks. As such, if the program reports a figure that does *not* appear to be in those units, the UNIX implementation on which it's running arguably has a bug or misfeature. The SVID - even the S5R4 Third Edition - says only that it's the number of "total free blocks", and doesn't indicate whether this means: sectors; 512-byte "blocks", even if your system has 1024-byte sectors (yes, there really *are* systems that have 1K sectors rather than 512-byte sectors); the native block size of the file system - or, in file systems with two "block" sizes, which one this is; for instance, is is the "block" size or the "fragment" size in a BSD file system? and it is quite possible that ISC, or somebody, chose some meaning other than the one that will, as indicated, make "ed" happy, namely "512-byte 'blocks'". (The S5R3.1 "ed" - and, I think, most "ed"s prior to that, and probably the S5R3.2 one as well - shift the file size right by 9 bits before comparing it with the "f_tfree", so that "f_tfree" must be in units of 512-byte chunks for this to work.) It is also possible that they misimplemented the way "ustat" on an NFS file system deals with the results of the STATFS call required for "ustat". What it should do is multiply "bavail" (since that's the number of bytes that non-privileged users can use - "bfree" is the number of blocks "free", but in e.g. the BSD file system, only the super-user can use all of them; other users can use only "bavail" of them) by "bsize" and divide the result by 512 (or, alternatively, multiply it by "bsize/512"). (Yes, I know, NFSSRC4.0 doesn't quite do that. *Mea culpa.* 512 should be used instead of DEV_BSIZE; the comment in the code notes that AT&T was, shall we say, less-than-careful in specifying what the units actually were. Doing the multiplication before the division may be risky, too, if it can overflow; if you can have a file system with > 2 gigabyte on it, this is a potential problem.) The test program (which works just fine in SunOS 4.0.3) follows. It may have to be compiled in the "System V environment" on systems that have multiple environments (e.g., compile it with "/usr/5bin/cc" on SunOS). I've only compiled it under SunOS, so I don't know whether it'll work on a "normal" S5 system. NOTE: the current NFS protocol has no way of telling a client how many "file slots" - e.g., inodes - are free on a file system. As such, the "f_tinode" number will be bogus on an NFS file system; "ed" doesn't use that, though. ----------------------------------Cut Here------------------------------------- #include #include #include #include #include #include /* * If you have the ANSI C "strerror" routine, toss this declaration and * the later definition out. */ static char *strerror(/*int errnum*/); int main(argc, argv) int argc; char **argv; { struct stat statb; struct ustat ustatb; if (argc != 2) { (void) fprintf(stderr, "Usage: ustattst \n"); return 1; } if (stat(argv[1], &statb) < 0) { (void) fprintf(stderr, "ustattst: Can't stat %s: %s\n", argv[1], strerror(errno)); return 2; } if (ustat(statb.st_dev, &ustatb) < 0) { (void) fprintf(stderr, "ustattst: Can't do ustat on (%d, %d): %s\n", major(statb.st_dev), minor(statb.st_dev), strerror(errno)); return 2; } (void) printf("tfree %ld tinode %ld fname %.6s fpack %.6s\n", ustatb.f_tfree, ustatb.f_tinode, ustatb.f_fname, ustatb.f_fpack); return 0; } extern int sys_nerr; extern char *sys_errlist[]; static char * strerror(errnum) int errnum; { static char errbuf[5+1+10+1]; /* "Error %d\0" */ if (errnum > 0 && errnum < sys_nerr) return sys_errlist[errnum]; else { (void) sprintf(errbuf, "Error %d", errnum); return errbuf; } }