Date: Tue, 25 Feb 2003 14:11:39 +0000 From: "Richard Dawe" Sender: rich AT phekda DOT freeserve DOT co DOT uk To: djgpp-workers AT delorie DOT com X-Mailer: Emacs 21.3.50 (via feedmail 8.3.emacs20_6 I) and Blat ver 1.8.6 Subject: fchmod, revision 2 [PATCH] Message-Id: Reply-To: djgpp-workers AT delorie DOT com Hello. Below is a patch for revision 2 of the implementation of fchmod. Bye, Rich =] Index: include/sys/stat.h =================================================================== RCS file: /cvs/djgpp/djgpp/include/sys/stat.h,v retrieving revision 1.8 diff -p -c -3 -r1.8 stat.h *** include/sys/stat.h 4 Feb 2003 20:25:54 -0000 1.8 --- include/sys/stat.h 25 Feb 2003 14:06:59 -0000 *************** struct stat { *** 63,68 **** --- 63,69 ---- }; int chmod(const char *_path, mode_t _mode); + int fchmod(int _fildes, mode_t _mode); int fstat(int _fildes, struct stat *_buf); int mkdir(const char *_path, mode_t _mode); int mkfifo(const char *_path, mode_t _mode); Index: include/sys/fsext.h =================================================================== RCS file: /cvs/djgpp/djgpp/include/sys/fsext.h,v retrieving revision 1.9 diff -p -c -3 -r1.9 fsext.h *** include/sys/fsext.h 24 Feb 2003 21:05:16 -0000 1.9 --- include/sys/fsext.h 25 Feb 2003 14:07:08 -0000 *************** typedef enum { *** 45,51 **** __FSEXT_symlink, __FSEXT_fchown, __FSEXT_chmod, ! __FSEXT_chown } __FSEXT_Fnumber; /* _ready gets passed a fd and should return a mask of these, --- 45,52 ---- __FSEXT_symlink, __FSEXT_fchown, __FSEXT_chmod, ! __FSEXT_chown, ! __FSEXT_fchmod } __FSEXT_Fnumber; /* _ready gets passed a fd and should return a mask of these, Index: src/libc/posix/sys/stat/makefile =================================================================== RCS file: /cvs/djgpp/djgpp/src/libc/posix/sys/stat/makefile,v retrieving revision 1.4 diff -p -c -3 -r1.4 makefile *** src/libc/posix/sys/stat/makefile 17 Oct 2002 23:00:25 -0000 1.4 --- src/libc/posix/sys/stat/makefile 25 Feb 2003 14:07:08 -0000 *************** *** 1,9 **** --- 1,11 ---- + # Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details # Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details # Copyright (C) 2000 DJ Delorie, see COPYING.DJ for details # Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details TOP=../../.. SRC += chmod.c + SRC += fchmod.c SRC += filelen.c SRC += fixpath.c SRC += fstat.c Index: src/libc/posix/sys/stat/chmod.txh =================================================================== RCS file: /cvs/djgpp/djgpp/src/libc/posix/sys/stat/chmod.txh,v retrieving revision 1.4 diff -p -c -3 -r1.4 chmod.txh *** src/libc/posix/sys/stat/chmod.txh 24 Feb 2003 21:05:46 -0000 1.4 --- src/libc/posix/sys/stat/chmod.txh 25 Feb 2003 14:07:15 -0000 *************** the following: *** 18,32 **** @item S_IRUSR ! Make the file readable @item S_IWUSR ! Make the file writable @end table ! Other @code{S_I*} values could be included, but they will be ignored. This function can be hooked by File System Extensions (@pxref{File System Extensions}). --- 18,58 ---- @item S_IRUSR ! Make the file readable for the owner. @item S_IWUSR ! Make the file writable for the owner. ! ! @item S_IRGRP ! ! Make the file readable for the group. ! ! @item S_IWGRP ! ! Make the file writeable for the group. ! ! @item S_IROTH ! ! Make the file readable for the world. ! ! @item S_IWOTH ! ! Make the file writeable for the world. @end table ! Some @code{S_I*} constants are ignored for regular files: ! ! @itemize @bullet ! @item ! @code{S_I*GRP} and @code{S_I*OTH} are ignored, because DOS/Windows ! has no concept of ownership, so all files are considered to belong ! to the user; ! ! @item ! @code{S_IR*} are ignored, because files are always readable on DOS/Windows. ! @end itemize This function can be hooked by File System Extensions (@pxref{File System Extensions}). Index: src/docs/kb/wc204.txi =================================================================== RCS file: /cvs/djgpp/djgpp/src/docs/kb/wc204.txi,v retrieving revision 1.142 diff -p -c -3 -r1.142 wc204.txi *** src/docs/kb/wc204.txi 24 Feb 2003 21:06:42 -0000 1.142 --- src/docs/kb/wc204.txi 25 Feb 2003 14:07:20 -0000 *************** will inline to a single instruction when *** 895,897 **** --- 895,900 ---- @cindex File System Extensions, and @code{chmod} @cindex File System Extensions, and @code{chown} @code{chmod} and @code{chown} can now be hooked by File System Extensions. + + @findex fchmod + The function @code{fchmod} was added. Index: tests/libc/posix/sys/stat/makefile =================================================================== RCS file: /cvs/djgpp/djgpp/tests/libc/posix/sys/stat/makefile,v retrieving revision 1.4 diff -p -c -3 -r1.4 makefile *** tests/libc/posix/sys/stat/makefile 22 Aug 2000 18:57:32 -0000 1.4 --- tests/libc/posix/sys/stat/makefile 25 Feb 2003 14:07:27 -0000 *************** *** 1,5 **** --- 1,6 ---- TOP=../../.. + SRC += t-fchmod.c SRC += fixpath.c SRC += fstat.c SRC += leak.c Index: src/libc/fsext/fse_zero.c =================================================================== RCS file: /cvs/djgpp/djgpp/src/libc/fsext/fse_zero.c,v retrieving revision 1.4 diff -p -c -3 -r1.4 fse_zero.c *** src/libc/fsext/fse_zero.c 24 Feb 2003 21:07:26 -0000 1.4 --- src/libc/fsext/fse_zero.c 25 Feb 2003 14:07:28 -0000 *************** dev_fsext (__FSEXT_Fnumber n, int *rv, v *** 576,581 **** --- 576,601 ---- *rv = -1; break; + case __FSEXT_fchmod: + fd = va_arg(args, int); + mode = va_arg(args, mode_t); + + /* This must be emulated, since the FSEXT has been called. */ + emul = 1; + + /* Get context */ + data = (DEV_DATA *) __FSEXT_get_data(fd); + if (data == NULL) { + errno = EBADF; + *rv = -1; + break; + } + + /* The zero device cannot have its mode changed. */ + errno = EPERM; + *rv = -1; + break; + case __FSEXT_chown: filename = va_arg(args, char *); owner = va_arg(args, uid_t); Index: tests/libc/fsext/tzero.c =================================================================== RCS file: /cvs/djgpp/djgpp/tests/libc/fsext/tzero.c,v retrieving revision 1.3 diff -p -c -3 -r1.3 tzero.c *** tests/libc/fsext/tzero.c 24 Feb 2003 21:07:26 -0000 1.3 --- tests/libc/fsext/tzero.c 25 Feb 2003 14:07:34 -0000 *************** main (int argc, char *argv[]) *** 431,436 **** --- 431,455 ---- close(fd); + /* - Check fchmod() fails - */ + fd = open(DEV_ZERO_PATH, O_RDWR); + if (fd == -1) { + fprintf(stderr, + "Unable to open %s: %s\n", DEV_ZERO_PATH, strerror(errno)); + return(EXIT_FAILURE); + } + + n = fchmod(fd, S_IWUSR); + if (n >= 0) { + fprintf(stderr, + "fchmod() succeeded in changing permissions of %s -" + "it should fail\n", + DEV_ZERO_PATH); + return(EXIT_FAILURE); + } + + close(fd); + /* - Check fchown() - */ /* fchown() should behave the same way for /dev/zero as it does for *** /dev/null Tue Feb 25 14:10:31 2003 --- src/libc/posix/sys/stat/fchmod.c Tue Feb 25 12:13:44 2003 *************** *** 0 **** --- 1,79 ---- + /* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */ + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + static int + get_current_mode (const int fd) + { + __dpmi_regs r; + int mode = 0; /* Fail by default */ + + if (_USE_LFN) { + r.x.ax = 0x71a6; /* File info by handle */ + r.x.bx = fd; + r.x.ds = __tb >> 4; + r.x.dx = 0; + + __dpmi_int(0x21, &r); + + if ((r.x.flags & 1) == 0) { + int attr = _farpeekl(_dos_ds, __tb); + + mode = S_IRUSR; /* Files are always readable. */ + if ((attr & 1) == 0) + mode |= S_IWUSR; + } + } + + return(mode); + } + + int + fchmod (int fd, mode_t mode) + { + __FSEXT_Function *func = __FSEXT_get_function(fd); + const char *filename = __get_fd_name(fd); + int dev_info; + int current_mode; + int rv; + + if ( func + && __FSEXT_func_wrapper(func, __FSEXT_fchmod, &rv, fd, mode)) + return(rv); + + /* Check that it's a valid file descriptor. */ + dev_info = _get_dev_info(fd); + if (dev_info == -1) + return(-1); + + /* Is this a character device? If so, silently ignore the request. */ + if (dev_info & _DEV_CDEV) + return 0; + + /* Get the current mode. If it's the same as those requested, + * just return. */ + /* NB: Only implemented toggle is write/nowrite */ + current_mode = get_current_mode(fd); + if (current_mode && (current_mode == (mode & (S_IRUSR|S_IWUSR)))) + return 0; + + /* It's not a device and we don't have the filename. So we can only + * fail. */ + if (filename == NULL) { + errno = ENOSYS; + return(-1); + } + + return(chmod(filename, mode)); + } *** /dev/null Tue Feb 25 14:10:31 2003 --- src/libc/posix/sys/stat/fchmod.txh Tue Feb 25 13:58:20 2003 *************** *** 0 **** --- 1,58 ---- + @node fchmod, file system + @findex fchmod + @subheading Syntax + + @example + #include + + int fchmod(int fd, mode_t mode); + @end example + + @subheading Description + + This function changes the mode (writable or write-only) of the file + opened under the file descriptor @var{fd}. The value of @var{mode} + can be a combination of one or more of the @code{S_I*} constants + described in the description of the @code{chmod} function (@pxref{chmod}). + + Some @code{S_I*} constants are ignored for regular files: + + @itemize @bullet + @item + @code{S_I*GRP} and @code{S_I*OTH} are ignored, because DOS/Windows + has no concept of ownership, so all files are considered to belong + to the user; + + @item + @code{S_IR*} are ignored, because files are always readable on DOS/Windows. + @end itemize + + @code{fchmod} will always succeed for character devices, but the mode + will be ignored. + + @code{fchmod} may not be able to change the mode of files that have been + opened using low-level methods. High-level methods for opening files + include the @code{fopen} (@pxref{fopen}) and @code{open} (@pxref{open}) + functions. Low-level methods include the @code{_open} (@pxref{_open}) + and @code{_dos_open} (@pxref{_dos_open}) functions. In particular, + redirected handles cannot have their mode changed with @code{fchmod}. + + This function can be hooked by File System Extensions + (@pxref{File System Extensions}). + + @subheading Return Value + + Zero if the file exists and the mode was changed, else nonzero. + + @subheading Portability + + @portability !ansi, posix + + @subheading Example + + @example + int fd; + + fd = open("/tmp/dj.dat", O_RDWR); + fchmod(fd, S_IWUSR|S_IRUSR); + @end example *** /dev/null Tue Feb 25 14:10:31 2003 --- tests/libc/posix/sys/stat/t-fchmod.c Thu Feb 20 14:17:52 2003 *************** *** 0 **** --- 1,186 ---- + #include + #include + #include + #include + #include + #include + #include + + const char TEST_FILE[] = "t-fchmod.tst"; + + typedef struct { + FILE *fp; + const char *name; + const mode_t mode; + } std_test_t; + + /* Try setting the handles to the mode they should have anyway. */ + std_test_t passing_std_tests[] = { + { stdin, "stdin", S_IRUSR }, + { stdout, "stdout", S_IWUSR }, + { stderr, "stderr", S_IWUSR }, + { stdaux, "stdaux", S_IRUSR|S_IWUSR }, + { stdprn, "stdprn", S_IWUSR }, + { NULL, NULL, 0 } + }; + + /* Try setting the handles to the mode they shouldn't have. */ + std_test_t failing_std_tests[] = { + { stdin, "stdin", S_IWUSR }, + { stdout, "stdout", S_IRUSR }, + { stderr, "stderr", S_IRUSR }, + #if 0 + { stdaux, "stdaux", 0 }, + { stdprn, "stdprn", S_IRUSR }, + #endif + { NULL, NULL, 0 } + }; + + static void + die (const char *prog_name) + { + perror(prog_name); + puts("FAIL"); + exit(EXIT_FAILURE); + } + + static int + check_read_only (const char *file) + { + if (access(file, R_OK)) + { + printf("%s should be readable!\n", file); + return(0); + } + + if (!access(file, W_OK)) + { + printf("%s should not be writeable!\n", file); + return(0); + } + + return(1); + } + + int + main (int argc, char *argv[]) + { + int ok = 1; + int fd; + int i; + + /* - Standard handles - */ + + for (i = 0; passing_std_tests[i].fp != NULL; i++) + { + if (fchmod(fileno(passing_std_tests[i].fp), + passing_std_tests[i].mode) < 0) + { + perror(passing_std_tests[i].name); + ok = 0; + } + } + + for (i = 0; failing_std_tests[i].fp != NULL; i++) + { + int std_fd = fileno(failing_std_tests[i].fp); + + if ( (fchmod(std_fd, failing_std_tests[i].mode) == 0) + && !isatty(std_fd)) + { + printf("%s: Should not be able to set mode to 0%o\n", + failing_std_tests[i].name, failing_std_tests[i].mode); + ok = 0; + } + } + + /* - Conventional file - */ + + /* Remove the test file, if it exists. */ + if (!access(TEST_FILE, F_OK) && unlink(TEST_FILE)) + die(argv[0]); + + /* Create the file with read-write permissions. */ + fd = open(TEST_FILE, O_BINARY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR); + if (fd < 0) + die(argv[0]); + close(fd); + + /* Check the permissions are read-write. */ + if (access(TEST_FILE, R_OK) || access(TEST_FILE, W_OK)) + { + printf("%s should be readable and writeable!\nFAIL\n", TEST_FILE); + return(EXIT_FAILURE); + } + + /* Re-open the file and change the permissions to read-only. + * Try writing to the file before and after. */ + fd = open(TEST_FILE, O_BINARY|O_RDWR); + if (fd < 0) + die(argv[0]); + + /* Try writing to the file. */ + { + char wibble[] = "wibble-read-write"; + + if (write(fd, wibble, strlen(wibble)) < 0) + { + printf("Unable to write to read-write file %s\n", TEST_FILE); + perror(argv[0]); + ok = 0; + } + } + + if (fchmod(fd, S_IRUSR) < 0) + die(argv[0]); + + /* Check the permissions are read-only. */ + if (!check_read_only(TEST_FILE)) + ok = 0; + + /* Try writing to the file. */ + { + char wibble[] = "wibble-read-only"; + + if (write(fd, wibble, strlen(wibble)) < 0) + { + printf("Unable to write to read-only file %s\n", TEST_FILE); + perror(argv[0]); + ok = 0; + } + } + + close(fd); + + /* Check the permissions are read-only. */ + if (!check_read_only(TEST_FILE)) + ok = 0; + + /* - Bypass normal open calls to get a file handle. */ + if (_dos_open(TEST_FILE, O_RDONLY, &fd) < 0) + die(argv[0]); + + /* Test that fchmod just returns, when the file permissions are + * the same as the current and there is no filename in fd_props. */ + if (fchmod(fd, S_IRUSR) < 0) + die(argv[0]); + + /* Check that it fails, when we try different file permissions. */ + if (fchmod(fd, S_IRUSR|S_IWUSR) >= 0) + { + printf("fchmod call should have failed: %s:%d!\n", __FILE__, __LINE__); + ok = 0; + } + + _dos_close(fd); + + /* - Done - */ + + if (!ok) { + puts("FAIL"); + return(EXIT_FAILURE); + } + + puts("PASS"); + return(EXIT_SUCCESS); + }