Sender: rich AT phekda DOT freeserve DOT co DOT uk Message-ID: <3BE2B26D.CA65166B@phekda.freeserve.co.uk> Date: Fri, 02 Nov 2001 14:49:17 +0000 From: Richard Dawe X-Mailer: Mozilla 4.77 [en] (X11; U; Linux 2.2.19 i586) X-Accept-Language: de,fr MIME-Version: 1.0 To: DJGPP workers Subject: Patch to add st_blocks to struct stat References: Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Reply-To: djgpp-workers AT delorie DOT com Hello. A long time ago in a galaxy very, very close to here, Eli Zaretskii wrote: > The logical step would be to add code to `stat' that computes the > st_blocks field (it is not in `struct stat', so you will need to > change the definition of `struct stat' as well). This could be done > by computing the cluster size for each drive letter the first time you > see that drive, and caching that value for future use. Then you can > round up the file's size using that cluster size. > > Once we support st_blocks, the changes to `du' to use it would be > trivial. Ideally, the `configure' script should detect that and cause > `du' to use it automagically. Please find below a patch that adds and calculates st_blocks in struct stat. Please note that the patch to fd_props.h may report some fuzz, when applied. I hacked out a small diff, which I had added when I started trying to add LFS support to DJGPP. Here's how the patch works: xstat.c now has a cache for storing the block size on various drive letters. The first time a file on, say, C: is stat'd, statvfs is called on the filename. The resulting block size is stored and then returned for all files on C: in the future. Note that _fixpath is called in the caching mechanism, so it should cope with joined drives, I think. I added a function to fd_props.h to return the filename, given a handle. This is needed in fstat, so that we know which drive the file is on. One problem I can think of is that there is no aging out in the cache. So if we map and unmap a network drive during the execution of a program which has stat'd the original mapping, the program will not notice. Is this really a problem? Now to the original motivation: fileutils does detect that we have st_blocks and then it uses it. Unfortunately du 4.0 rounds file sizes down, so if you have a big block size (like I seem to on my HD) and you ask for a size shown in bytes, then the file sizes are bogus (e.g. 0 for files < 16K on my HD). du 4.1 rounds up file sizes, so it should be OK. Same st_blocks values: My 24GB DOS partition: 16 Kbytes Mapped network drive (Samba share from Linux box): 32 Kbytes 1.44MB floppy: 512 bytes Oh yeah, I also fixed the *stat test programs a bit - include needed headers, return values from main - and made them display block information. OK to commit? Thanks, bye, Rich =] -- Richard Dawe http://www.phekda.freeserve.co.uk/richdawe/ Index: include/sys/types.h =================================================================== RCS file: /cvs/djgpp/djgpp/include/sys/types.h,v retrieving revision 1.6 diff -p -c -3 -r1.6 types.h *** include/sys/types.h 2000/12/05 14:05:53 1.6 --- include/sys/types.h 2001/11/02 14:36:20 *************** extern "C" { *** 13,19 **** #ifndef __STRICT_ANSI__ #include ! typedef int dev_t; typedef int ino_t; typedef int mode_t; --- 13,21 ---- #ifndef __STRICT_ANSI__ #include ! ! typedef int blkcnt_t; ! typedef int blksize_t; typedef int dev_t; typedef int ino_t; typedef int mode_t; Index: include/sys/stat.h =================================================================== RCS file: /cvs/djgpp/djgpp/include/sys/stat.h,v retrieving revision 1.4 diff -p -c -3 -r1.4 stat.h *** include/sys/stat.h 2000/12/05 14:05:53 1.4 --- include/sys/stat.h 2001/11/02 14:36:24 *************** struct stat { *** 49,57 **** time_t st_mtime; nlink_t st_nlink; off_t st_size; ! off_t st_blksize; uid_t st_uid; dev_t st_rdev; /* unused */ }; int chmod(const char *_path, mode_t _mode); --- 49,58 ---- time_t st_mtime; nlink_t st_nlink; off_t st_size; ! blksize_t st_blksize; uid_t st_uid; dev_t st_rdev; /* unused */ + blkcnt_t st_blocks; }; int chmod(const char *_path, mode_t _mode); Index: include/libc/fd_props.h =================================================================== RCS file: /cvs/djgpp/djgpp/include/libc/fd_props.h,v retrieving revision 1.6 diff -p -c -3 -r1.6 fd_props.h *** include/libc/fd_props.h 2001/06/06 21:09:50 1.6 --- include/libc/fd_props.h 2001/11/02 14:36:27 *************** extern "C" { *** 8,13 **** --- 8,15 ---- #ifndef __dj_ENFORCE_ANSI_FREESTANDING + #include + #ifndef __STRICT_ANSI__ #ifndef _POSIX_SOURCE *************** static __inline__ void __clear_fd_flags( *** 63,68 **** --- 68,78 ---- static __inline__ unsigned long __get_fd_flags(int _fd) { return __has_fd_properties(_fd) ? __fd_properties[_fd]->flags : 0; + } + + static __inline__ const char * __get_fd_name(int _fd) + { + return __has_fd_properties(_fd) ? __fd_properties[_fd]->filename : NULL; } #endif /* !_POSIX_SOURCE */ Index: src/libc/posix/sys/stat/fstat.c =================================================================== RCS file: /cvs/djgpp/djgpp/src/libc/posix/sys/stat/fstat.c,v retrieving revision 1.7 diff -p -c -3 -r1.7 fstat.c *** src/libc/posix/sys/stat/fstat.c 2001/09/25 01:00:52 1.7 --- src/libc/posix/sys/stat/fstat.c 2001/11/02 14:36:34 *************** *** 106,111 **** --- 106,112 ---- #include #include #include + #include #include #include *************** fstat_assist(int fhandle, struct stat *s *** 434,440 **** stat_buf->st_gid = getgid(); stat_buf->st_nlink = 1; #ifndef NO_ST_BLKSIZE ! stat_buf->st_blksize = _go32_info_block.size_of_transfer_buffer; #endif /* If SFT entry for our handle is required and available, we will use it. */ --- 435,451 ---- stat_buf->st_gid = getgid(); stat_buf->st_nlink = 1; #ifndef NO_ST_BLKSIZE ! if (__get_fd_name(fhandle)) ! { ! const char *filename = __get_fd_name(fhandle); ! stat_buf->st_blksize = _get_cached_blksize(filename); ! } ! else ! { ! /* Fall back on transfer buffer size, if we can't determine file name ! * (which gives the drive letter and then the drive's cluster size). */ ! stat_buf->st_blksize = _go32_info_block.size_of_transfer_buffer; ! } #endif /* If SFT entry for our handle is required and available, we will use it. */ *************** fstat_assist(int fhandle, struct stat *s *** 717,722 **** --- 728,738 ---- /* Additional time info for LFN platforms. */ set_fstat_times (fhandle, stat_buf); + + /* Number of blocks */ + stat_buf->st_blocks = _get_blkcnt(stat_buf->st_size, + stat_buf->st_blksize); + return 0; } *************** fstat_assist(int fhandle, struct stat *s *** 856,861 **** --- 872,881 ---- /* Additional time info for LFN platforms. */ set_fstat_times (fhandle, stat_buf); + + /* Number of blocks */ + stat_buf->st_blocks = _get_blkcnt(stat_buf->st_size, + stat_buf->st_blksize); } return 0; } *************** int main(int argc, char *argv[]) *** 936,941 **** --- 956,964 ---- _djstat_describe_lossage(stderr); } + if (!argc) + return EXIT_SUCCESS; + /* Now call fstat() for each command-line argument. */ while (++argv, --argc) { *************** int main(int argc, char *argv[]) *** 956,961 **** --- 979,987 ---- fprintf (stderr, "\t\t\tTimes: %lu %lu\n", (unsigned long)stat_buf.st_atime, (unsigned long)stat_buf.st_ctime); + fprintf(stderr, "\t\t\tBlocks: %d %d\n", + stat_buf.st_blksize, + stat_buf.st_blocks); _djstat_describe_lossage(stderr); } else *************** int main(int argc, char *argv[]) *** 965,971 **** _djstat_describe_lossage(stderr); } } ! return 0; } #endif /* TEST */ --- 991,997 ---- _djstat_describe_lossage(stderr); } } ! return EXIT_SUCCESS; } #endif /* TEST */ Index: src/libc/posix/sys/stat/lstat.c =================================================================== RCS file: /cvs/djgpp/djgpp/src/libc/posix/sys/stat/lstat.c,v retrieving revision 1.7 diff -p -c -3 -r1.7 lstat.c *** src/libc/posix/sys/stat/lstat.c 2001/10/17 05:08:39 1.7 --- src/libc/posix/sys/stat/lstat.c 2001/11/02 14:36:42 *************** stat_assist(const char *path, struct sta *** 445,451 **** statbuf->st_gid = getgid(); statbuf->st_nlink = 1; #ifndef NO_ST_BLKSIZE ! statbuf->st_blksize = _go32_info_block.size_of_transfer_buffer; #endif /* Make the path explicit. This makes the rest of our job much --- 445,451 ---- statbuf->st_gid = getgid(); statbuf->st_nlink = 1; #ifndef NO_ST_BLKSIZE ! statbuf->st_blksize = _get_cached_blksize(path); #endif /* Make the path explicit. This makes the rest of our job much *************** stat_assist(const char *path, struct sta *** 868,873 **** --- 868,876 ---- statbuf->st_size = (nfiles + extra) * sizeof(struct full_dirent); } + /* Number of blocks */ + statbuf->st_blocks = _get_blkcnt(statbuf->st_size, statbuf->st_blksize); + return 0; } *************** lstat(const char *path, struct stat *sta *** 932,940 **** #ifdef TEST unsigned short _djstat_flags = 0; ! void main(int argc, char *argv[]) { struct stat stat_buf; --- 935,945 ---- #ifdef TEST + #include + unsigned short _djstat_flags = 0; ! int main(int argc, char *argv[]) { struct stat stat_buf; *************** main(int argc, char *argv[]) *** 943,949 **** if (argc < 2) { fprintf (stderr, "Usage: %s <_djstat_flags> \n", argv[0]); ! exit(0); } if (lstat(*argv, &stat_buf) != 0) --- 948,954 ---- if (argc < 2) { fprintf (stderr, "Usage: %s <_djstat_flags> \n", argv[0]); ! return (EXIT_FAILURE); } if (lstat(*argv, &stat_buf) != 0) *************** main(int argc, char *argv[]) *** 968,973 **** --- 973,981 ---- (long)stat_buf.st_size, (unsigned long)stat_buf.st_mtime, ctime(&stat_buf.st_mtime)); + fprintf(stderr, "\t\t\tBlocks: %d %d\n", + stat_buf.st_blksize, + stat_buf.st_blocks); _djstat_describe_lossage(stderr); } else *************** main(int argc, char *argv[]) *** 980,986 **** ++argv; } ! exit (0); } #endif --- 988,994 ---- ++argv; } ! return (EXIT_SUCCESS); } #endif Index: src/libc/posix/sys/stat/xstat.c =================================================================== RCS file: /cvs/djgpp/djgpp/src/libc/posix/sys/stat/xstat.c,v retrieving revision 1.3 diff -p -c -3 -r1.3 xstat.c *** src/libc/posix/sys/stat/xstat.c 2000/08/05 16:53:46 1.3 --- src/libc/posix/sys/stat/xstat.c 2001/11/02 14:36:50 *************** *** 19,24 **** --- 19,27 ---- #include #include #include + #include + #include + #include #include #include #include *************** _getftime(int fhandle, unsigned int *dos *** 116,121 **** --- 119,176 ---- *dos_ftime = ((unsigned int)regs.x.dx << 16) + (unsigned int)regs.x.cx; return 0; + } + + /* Cache the cluster size (aka block size) for each drive letter, so we can + * populate the st_blksize of struct stat easily. The cluster size is + * measured in bytes. + */ + + /* Comment copied from DJGPP 2.03's src/libc/compat/mntent/mntent.c: + * + * There may be a maximum of 32 block devices. Novell Netware indeed + * allows for 32 disks (A-Z plus 6 more characters from '[' to '\'') + */ + static blksize_t cache_blksize[32]; + static int cache_blksize_count = -1; + + blksize_t + _get_cached_blksize (const char *path) + { + char fixed_path[PATH_MAX + 1]; + struct statfs sbuf; + int d; /* drive */ + static int overmax_d = sizeof(cache_blksize) / sizeof(cache_blksize[0]); + + /* Force initialization in restarted programs (emacs). */ + if (cache_blksize_count != __bss_count) + { + cache_blksize_count = __bss_count; + memset(cache_blksize, 0, sizeof(cache_blksize)); + } + + _fixpath(path, fixed_path); + d = tolower(path[0]) - 'a'; + + if ((d < 0) || (d >= overmax_d)) + { + errno = ENODEV; + return -1; + } + + if (!cache_blksize[d]) + { + /* No entry => retrieve cluster size */ + if (statfs(path, &sbuf) != 0) + { + /* Failed, pass error through */ + return -1; + } + + cache_blksize[d] = sbuf.f_bsize; + } + + return cache_blksize[d]; } /* Invent an inode number for those files which don't have valid DOS Index: src/libc/posix/sys/stat/stat.c =================================================================== RCS file: /cvs/djgpp/djgpp/src/libc/posix/sys/stat/stat.c,v retrieving revision 1.10 diff -p -c -3 -r1.10 stat.c *** src/libc/posix/sys/stat/stat.c 2000/08/22 18:45:07 1.10 --- src/libc/posix/sys/stat/stat.c 2001/11/02 14:36:53 *************** stat(const char *path, struct stat *stat *** 31,39 **** #ifdef TEST unsigned short _djstat_flags = 0; ! void main(int argc, char *argv[]) { struct stat stat_buf; --- 31,41 ---- #ifdef TEST + #include + unsigned short _djstat_flags = 0; ! int main(int argc, char *argv[]) { struct stat stat_buf; *************** main(int argc, char *argv[]) *** 42,48 **** if (argc < 2) { fprintf (stderr, "Usage: %s <_djstat_flags> \n", argv[0]); ! exit(0); } if (stat(*argv, &stat_buf) != 0) --- 44,50 ---- if (argc < 2) { fprintf (stderr, "Usage: %s <_djstat_flags> \n", argv[0]); ! return (EXIT_FAILURE); } if (stat(*argv, &stat_buf) != 0) *************** main(int argc, char *argv[]) *** 67,72 **** --- 69,77 ---- (long)stat_buf.st_size, (unsigned long)stat_buf.st_mtime, ctime(&stat_buf.st_mtime)); + fprintf(stderr, "\t\t\tBlocks: %d %d\n", + stat_buf.st_blksize, + stat_buf.st_blocks); _djstat_describe_lossage(stderr); } else *************** main(int argc, char *argv[]) *** 79,85 **** ++argv; } ! exit (0); } #endif --- 84,90 ---- ++argv; } ! return (EXIT_SUCCESS); } #endif Index: src/libc/posix/sys/stat/xstat.h =================================================================== RCS file: /cvs/djgpp/djgpp/src/libc/posix/sys/stat/xstat.h,v retrieving revision 1.4 diff -p -c -3 -r1.4 xstat.h *** src/libc/posix/sys/stat/xstat.h 2000/08/05 16:53:46 1.4 --- src/libc/posix/sys/stat/xstat.h 2001/11/02 14:36:57 *************** extern unsigned short _djstat_fail_bit *** 56,61 **** --- 56,76 ---- extern unsigned short _djstat_flags; + static inline blkcnt_t _get_blkcnt (off_t _st_size, blksize_t _st_blksize) + { + blkcnt_t _blkcnt; + + if (!_st_size) + return 0; + + /* Round up to the nearest block. */ + _blkcnt = _st_size / _st_blksize; + if ((_blkcnt * _st_blksize) < _st_size) + _blkcnt++; + + return _blkcnt; + } + extern time_t _file_time_stamp(unsigned int); extern ino_t _invent_inode(const char *, unsigned, unsigned long); extern unsigned short _get_magic(const char *, int); *************** extern long __filelength(int *** 68,72 **** --- 83,88 ---- extern int _is_remote_handle(int); extern void _djstat_describe_lossage(FILE *); extern int _getftime(int, unsigned int *); + extern blksize_t _get_cached_blksize (const char *path); #endif /* __XSTAT_H */ Index: src/libc/posix/sys/stat/lstat.txh =================================================================== RCS file: /cvs/djgpp/djgpp/src/libc/posix/sys/stat/lstat.txh,v retrieving revision 1.3 diff -p -c -3 -r1.3 lstat.txh *** src/libc/posix/sys/stat/lstat.txh 2001/08/01 10:31:02 1.3 --- src/libc/posix/sys/stat/lstat.txh 2001/11/02 14:37:06 *************** it in @var{sbuf}, which has this structu *** 14,30 **** @smallexample struct stat @{ ! time_t st_atime; /* time of last access */ ! time_t st_ctime; /* time of file's creation */ ! dev_t st_dev; /* The drive number (0 = a:) */ ! gid_t st_gid; /* what getgid() returns */ ! ino_t st_ino; /* starting cluster or unique identifier */ ! mode_t st_mode; /* file mode - S_IF* and S_IRUSR/S_IWUSR */ ! time_t st_mtime; /* time that the file was last written */ ! nlink_t st_nlink; /* 2 + number of subdirs, or 1 for files */ ! off_t st_size; /* size of file in bytes */ ! off_t st_blksize; /* the size of transfer buffer */ ! uid_t st_uid; /* what getuid() returns */ @}; @end smallexample --- 14,31 ---- @smallexample struct stat @{ ! time_t st_atime; /* time of last access */ ! time_t st_ctime; /* time of file's creation */ ! dev_t st_dev; /* The drive number (0 = a:) */ ! gid_t st_gid; /* what getgid() returns */ ! ino_t st_ino; /* starting cluster or unique identifier */ ! mode_t st_mode; /* file mode - S_IF* and S_IRUSR/S_IWUSR */ ! time_t st_mtime; /* time that the file was last written */ ! nlink_t st_nlink; /* 2 + number of subdirs, or 1 for files */ ! off_t st_size; /* size of file in bytes */ ! blksize_t st_blksize; /* block size in bytes*/ ! uid_t st_uid; /* what getuid() returns */ ! blkcnt_t st_blocks; /* blocks used by file */ @}; @end smallexample Index: src/libc/posix/sys/stat/stat.txh =================================================================== RCS file: /cvs/djgpp/djgpp/src/libc/posix/sys/stat/stat.txh,v retrieving revision 1.8 diff -p -c -3 -r1.8 stat.txh *** src/libc/posix/sys/stat/stat.txh 2001/08/01 10:31:02 1.8 --- src/libc/posix/sys/stat/stat.txh 2001/11/02 14:37:12 *************** it in @var{sbuf}, which has this structu *** 14,30 **** @smallexample struct stat @{ ! time_t st_atime; /* time of last access */ ! time_t st_ctime; /* time of file's creation */ ! dev_t st_dev; /* The drive number (0 = a:) */ ! gid_t st_gid; /* what getgid() returns */ ! ino_t st_ino; /* starting cluster or unique identifier */ ! mode_t st_mode; /* file mode - S_IF* and S_IRUSR/S_IWUSR */ ! time_t st_mtime; /* time that the file was last written */ ! nlink_t st_nlink; /* 2 + number of subdirs, or 1 for files */ ! off_t st_size; /* size of file in bytes */ ! off_t st_blksize; /* the size of transfer buffer */ ! uid_t st_uid; /* what getuid() returns */ @}; @end smallexample --- 14,31 ---- @smallexample struct stat @{ ! time_t st_atime; /* time of last access */ ! time_t st_ctime; /* time of file's creation */ ! dev_t st_dev; /* The drive number (0 = a:) */ ! gid_t st_gid; /* what getgid() returns */ ! ino_t st_ino; /* starting cluster or unique identifier */ ! mode_t st_mode; /* file mode - S_IF* and S_IRUSR/S_IWUSR */ ! time_t st_mtime; /* time that the file was last written */ ! nlink_t st_nlink; /* 2 + number of subdirs, or 1 for files */ ! off_t st_size; /* size of file in bytes */ ! blksize_t st_blksize; /* block size in bytes*/ ! uid_t st_uid; /* what getuid() returns */ ! blkcnt_t st_blocks; /* blocks used by file */ @}; @end smallexample Index: src/docs/kb/wc204.txi =================================================================== RCS file: /cvs/djgpp/djgpp/src/docs/kb/wc204.txi,v retrieving revision 1.100 diff -p -c -3 -r1.100 wc204.txi *** src/docs/kb/wc204.txi 2001/10/17 05:19:49 1.100 --- src/docs/kb/wc204.txi 2001/11/02 14:37:42 *************** case lowering. This replaces the functi *** 675,677 **** --- 675,686 ---- which is hopelessly buggy on Windows 2000 and XP. New function used in @file{srchpath.c}, @file{readdir.c}, @file{glob.c}, @file{fixpath.c}, @file{lstat.c} and @file{getcwd.c}. + + @findex stat{, and block size} + @findex lstat{, and block size} + @findex fstat AT r{, and block size} + The functions @code{stat}, @code{lstat} and @code{fstat} now fill + the @code{st_blksize} member of @code{struct stat} with the correct block + size for the device where the file is located. @code{struct stat} now + has a @code{st_blocks} member, which contains the number of blocks + occupied by the file.