Message-Id: <5.0.1.4.0.20001119021029.0235ee50@pop5.banet.net> X-Sender: usbanet DOT farley3 AT pop5 DOT banet DOT net X-Mailer: QUALCOMM Windows Eudora Version 5.0.1 Date: Sun, 19 Nov 2000 02:39:39 -0500 To: djgpp-workers AT delorie DOT com From: "Peter J. Farley III" Subject: Trial version of locking fcntl Cc: "Mark E." Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=====================_55779254==_" Reply-To: djgpp-workers AT delorie DOT com --=====================_55779254==_ Content-Type: text/plain; charset="us-ascii"; format=flowed OK, I have gotten Mark's locking version of fcntl into what looks to me like working order. I have attached a diff to the current v2.03 fcntl, a complete version and a small test program. Please test it on your systems. The test program just prints the return code from various calls to fcntl, it's not a very complete test yet but it is a small beginning. I will try to make up a more complete test program and post it later. As Mark did, I mainly used it to step through the code with gdb to find problems. I have built libc with this version and then built a recent bleedperl release (perl AT 7707) with the new libc. Perl seems to like it; perl uses the locking functions of fcntl to simulate flock and passes both basic perl flock tests and tests using the emulated flock in the Storable module tests. There was one locking-related patch needed to Storable.pm, and I have submitted that to the p5p list. The F_GETLK function is not yet working properly, but I hope to have that fixed soon. For those interested, the main change I made to Mark's version was the assignment to lock_req in the F_SETLK... section. Mark had coded this: va_start (ap, cmd); lock_req = va_arg(lock_req, struct flock *); va_end (ap); What was needed was this: va_start (ap, cmd); lock_req = va_arg(ap, struct flock *); va_end (ap); Notice the difference in the argument to va_arg. I also allowed F_RDLCK requests to proceed under the following conditions: lock_req->l_whence = SEEK_SET lock_req->l_start == 0 lock_req->l_len == 0 Under these conditions, F_RDLCK is equivalent to a file-level lock, so I saw no harm in allowing it. Comments, errata, etc. welcome. Enjoy. --=====================_55779254==_ Content-Type: text/plain; charset="us-ascii" Content-Disposition: attachment; filename="fcntl.dif" --- libc/posix/fcntl/fcntl.c0 Thu Jun 3 13:27:38 1999 +++ libc/posix/fcntl/fcntl.c Fri Nov 17 20:08:00 2000 @@ -1,3 +1,4 @@ +/* Copyright (C) 2000 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */ @@ -7,28 +8,101 @@ #include #include #include +#include #include +#include +#include +#include +#include -static int -is_used_fd(int fd) + +#include + + +static unsigned long _get_sft_entry_ptr(int fd) { __dpmi_regs regs; + unsigned char index; + unsigned long es, di; - regs.x.ax = 0x4400; + + /* Get the JFT entry address for this handle. */ + regs.x.ax = 0x1220; regs.x.bx = fd; - __dpmi_int(0x21, ®s); + __dpmi_int(0x2f, ®s); + + + if (regs.x.flags & 1) + { + errno = ENOSYS; + return 0; + } + + + /* Get the SFT entry number for this handle. */ + es = regs.x.es; + di = regs.x.di; + index = _farpeekb(_dos_ds, es * 16 + di); + + + /* Now get the address of the entry. */ + regs.x.ax = 0x1216; + regs.x.bx = index; + __dpmi_int (0x2f, ®s); if (regs.x.flags & 1) + { + errno = ENOSYS; return 0; + } + + + es = regs.x.es; + di = regs.x.di; + + + return es * 16 + di; +} + + +static int +inherit_bit_test (int fd, short dev_info) +{ + __dpmi_regs regs; + short new_dev_info; - return 1; + + dev_info |= _DEV_NO_INHERIT; + regs.x.ax = 0x4401; + regs.x.bx = fd; + regs.x.dx = dev_info; + __dpmi_int(0x21, ®s); + + new_dev_info = _get_dev_info(fd); + + /* If the dev info words are equal, then the documented + interface can't be used to set the inheritance bit. */ + return (new_dev_info == dev_info) ? 1 : 0; } + int fcntl(int fd, int cmd, ...) { int tofd, open_max; va_list ap; - __FSEXT_Function *func = __FSEXT_get_function(fd); + __FSEXT_Function *func; + short dev_info = _get_dev_info(fd); + static int inherit_bit_visible = -1; + int errno_save; + + /* Verify the descriptor is valid by retrieving + the handle's device info word. */ + if (dev_info == -1) + return dev_info; + + + /* Allow a fd to override with a FSEXT. */ + func = __FSEXT_get_function(fd); if (func) { int rv; @@ -36,62 +110,272 @@ return rv; } + + errno_save = errno; + switch (cmd) { - case F_DUPFD: - va_start(ap, cmd); - tofd = va_arg(ap, int); - va_end(ap); - - open_max = getdtablesize(); - if (tofd < 0 || tofd >= open_max) + case F_DUPFD: { - errno = EINVAL; - return -1; + va_start(ap, cmd); + tofd = va_arg(ap, int); + va_end(ap); + + + open_max = getdtablesize(); + while (tofd < open_max) + { + /* If unable to get the device info for the handle, + then the handle is not active and it can be used. */ + if (_get_dev_info(tofd) == -1) + break; + tofd++; + } + + + if (tofd >= open_max) + { + errno = EMFILE; + return -1; + } + + + errno = errno_save; + return dup2(fd, tofd); } - while (tofd < open_max) + + + case F_GETFD: { - if (! is_used_fd(tofd)) - break; - tofd++; + unsigned long entry_ptr; + + + /* DOS only passes the first 20 handles to child programs. In + addition, handles 19 and 18 will be closed by the stub of the + child program (if it is a DJGPP program). */ + + + if (fd >= 18) + return FD_CLOEXEC; + + + /* Determine if the documented interface will allow twiddling with + the inherit bit. If not, fallback to the undocumented one. */ + if (inherit_bit_visible == -1) + inherit_bit_visible = inherit_bit_test (fd, dev_info); + + if (!inherit_bit_visible) + { + entry_ptr = _get_sft_entry_ptr(fd); + if (entry_ptr == 0) + { + /* The fd has already been validated, so reaching here means + something is wrong with _get_sft_entry_ptr. */ + return -1; + } + /* Offset 5 in the SFT contains the device info word. */ + dev_info = _farpeekw(_dos_ds, entry_ptr + 5); + } + return (dev_info & _DEV_NO_INHERIT) ? FD_CLOEXEC : 0; } - if (tofd >= open_max) + + case F_SETFD: { - errno = EMFILE; - return -1; + unsigned int flag; + unsigned long entry_ptr = 0; /* shut up -Wall */ + __dpmi_regs regs; + + + va_start (ap, cmd); + flag = va_arg(ap, int); + va_end(ap); + + + /* DOS only passes the first 20 handles to child programs. In + addition, handles 19 and 18 will be closed by the stub of the + child program (if it is a DJGPP program). */ + if (fd >= 18) + { + if (flag & FD_CLOEXEC) + return 0; + else + { + errno = ENOSYS; + return -1; + } + } + + + /* Determine if the documented interface will allow twiddling with + the inherit bit. If not, fallback to the undocumented one. */ + if (inherit_bit_visible == -1) + inherit_bit_visible = inherit_bit_test(fd, dev_info); + + if (!inherit_bit_visible) + { + entry_ptr = _get_sft_entry_ptr(fd); + if (entry_ptr == 0) + { + /* The fd has already been validated, so reaching here means + something is wrong with _get_sft_entry_ptr. */ + return -1; + } + + + dev_info = _farpeekw(_dos_ds, entry_ptr + 5); + } + + + if (flag & FD_CLOEXEC) + dev_info |= _DEV_NO_INHERIT; + else + dev_info &= ~(_DEV_NO_INHERIT); + + + if (inherit_bit_visible) + { + regs.x.ax = 0x4401; + regs.x.bx = fd; + regs.x.dx = dev_info; + __dpmi_int(0x21, ®s); + } + else + _farpokew(_dos_ds, entry_ptr + 5, dev_info); + + + return 0; } - return dup2(fd, tofd); - - case F_GETFD: - /* DOS only passes the first 20 handles to child programs. In - addition, handles 19 and 18 will be closed by the stub of the - child program (if it is a DJGPP program). - FIXME: we should look at the no-inherit bit stashed in the SFT - entry pointed to by the handle, since some of the first 18 - handles could have been opened with a no-inherit bit. */ - return fd >= 18 ? FD_CLOEXEC : 0; - case F_SETFD: - if ((fd < 18) ^ ((cmd & FD_CLOEXEC) != 0)) + case F_GETFL: + { return 0; - else + } + + + case F_SETFL: + { + unsigned char new_mode_bits; + + + va_start (ap, cmd); + new_mode_bits = va_arg(ap,int); + va_end(ap); + + + /* Allow removal of O_NONBLOCK, since DJGPP doesn't support it + anyway. */ + return (new_mode_bits == 0) ? 0 : -1; + } + + + case F_GETLK: + case F_SETLK: + case F_SETLKW: + { + struct flock *lock_req = NULL; /* shut up -Wall */ + int ret = -1; + off_t pos, cur_pos, lock_pos; + off_t len; + + + cur_pos = lseek(fd, 0, SEEK_CUR); + va_start (ap, cmd); + lock_req = va_arg(ap, struct flock *); + va_end (ap); + lock_pos = lseek (fd, lock_req->l_start, lock_req->l_whence); + len = lock_req->l_len; + + + /* If l_len is zero, then the lock is to be set from l_start + until the end-of-file. */ + if (len == 0) { - errno = ENOSYS; - return -1; + len = filelength(fd) - cur_pos; + /* This should probably be an error. */ + if (len <= 0) + len = 1; } - case F_GETFL: - return 0; /* FIXME: should use the data in the SFT */ - case F_SETFL: - errno = ENOSYS; - return -1; - case F_GETLK: - case F_SETLK: - case F_SETLKW: - errno = ENOSYS; - return -1; + + + lseek (fd, cur_pos, SEEK_SET); + + + /* If l_len is positive, the area to lock is from l_start + to l_start + l_len - 1. If l_len is negative, the area to lock is + from l_start + len to l_start - 1. */ + if (len > 0) + { + pos = lock_pos; + } + else + { + pos = lock_pos + len; + len = -lock_req->l_len; + } + + + /* DOS/Windows only support read locks on a per-file basis, + so any attempted use of a read lock is an error, + UNLESS all of these conditions are true, which makes the + request into a per-file lock equivalent to F_WRLCK: + + lock_req->l_whence = SEEK_SET + lock_req->l_start == 0 + lock_req->l_len == 0 + */ + if ((lock_req->l_type == F_RDLCK) && + (lock_req->l_whence != SEEK_SET || + lock_req->l_start != 0 || + lock_req->l_len != 0)) + { + errno = ENOSYS; + return -1; + } + + if (lock_req->l_type == F_UNLCK) + { + ret = _dos_unlock(fd, pos, len); + } + else if ((lock_req->l_type == F_WRLCK) || (lock_req->l_type == F_RDLCK)) + { + ret = _dos_lock(fd, pos, len); + if (cmd == F_GETLK) + { + if (ret == 0) + { + _dos_unlock(fd, pos, len); + /* If no lock is found that would prevent a lock from + being created, the lock type is set to F_UNLCK. */ + lock_req->l_type = F_UNLCK; + } + else + { + /* If a lock is found then l_whence, l_start, and l_len + should point to the area covered by the lock. But the + file locking interface doesn't report where the + existing lock is, so nothing more can be done. */ + ; + } + } + + + /* If F_SETLKW is set, wait for the lock to be released. */ + if (cmd == F_SETLKW && ret < 0) + while ((ret = _dos_lock(fd, pos, len)) < 0) + __dpmi_yield(); + } + if (ret < 0) + errno = ENOSYS; + + + return ret; + } } + + + /* In case fcntl is called with an unrecognized command. */ errno = ENOSYS; return -1; } --=====================_55779254==_ Content-Type: text/plain; charset="us-ascii" Content-Disposition: attachment; filename="fcntl.c" /* Copyright (C) 2000 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */ #include #include #include #include #include #include #include #include #include #include #include #include #include static unsigned long _get_sft_entry_ptr(int fd) { __dpmi_regs regs; unsigned char index; unsigned long es, di; /* Get the JFT entry address for this handle. */ regs.x.ax = 0x1220; regs.x.bx = fd; __dpmi_int(0x2f, ®s); if (regs.x.flags & 1) { errno = ENOSYS; return 0; } /* Get the SFT entry number for this handle. */ es = regs.x.es; di = regs.x.di; index = _farpeekb(_dos_ds, es * 16 + di); /* Now get the address of the entry. */ regs.x.ax = 0x1216; regs.x.bx = index; __dpmi_int (0x2f, ®s); if (regs.x.flags & 1) { errno = ENOSYS; return 0; } es = regs.x.es; di = regs.x.di; return es * 16 + di; } static int inherit_bit_test (int fd, short dev_info) { __dpmi_regs regs; short new_dev_info; dev_info |= _DEV_NO_INHERIT; regs.x.ax = 0x4401; regs.x.bx = fd; regs.x.dx = dev_info; __dpmi_int(0x21, ®s); new_dev_info = _get_dev_info(fd); /* If the dev info words are equal, then the documented interface can't be used to set the inheritance bit. */ return (new_dev_info == dev_info) ? 1 : 0; } int fcntl(int fd, int cmd, ...) { int tofd, open_max; va_list ap; __FSEXT_Function *func; short dev_info = _get_dev_info(fd); static int inherit_bit_visible = -1; int errno_save; /* Verify the descriptor is valid by retrieving the handle's device info word. */ if (dev_info == -1) return dev_info; /* Allow a fd to override with a FSEXT. */ func = __FSEXT_get_function(fd); if (func) { int rv; if (func(__FSEXT_fcntl, &rv, &fd)) return rv; } errno_save = errno; switch (cmd) { case F_DUPFD: { va_start(ap, cmd); tofd = va_arg(ap, int); va_end(ap); open_max = getdtablesize(); while (tofd < open_max) { /* If unable to get the device info for the handle, then the handle is not active and it can be used. */ if (_get_dev_info(tofd) == -1) break; tofd++; } if (tofd >= open_max) { errno = EMFILE; return -1; } errno = errno_save; return dup2(fd, tofd); } case F_GETFD: { unsigned long entry_ptr; /* DOS only passes the first 20 handles to child programs. In addition, handles 19 and 18 will be closed by the stub of the child program (if it is a DJGPP program). */ if (fd >= 18) return FD_CLOEXEC; /* Determine if the documented interface will allow twiddling with the inherit bit. If not, fallback to the undocumented one. */ if (inherit_bit_visible == -1) inherit_bit_visible = inherit_bit_test (fd, dev_info); if (!inherit_bit_visible) { entry_ptr = _get_sft_entry_ptr(fd); if (entry_ptr == 0) { /* The fd has already been validated, so reaching here means something is wrong with _get_sft_entry_ptr. */ return -1; } /* Offset 5 in the SFT contains the device info word. */ dev_info = _farpeekw(_dos_ds, entry_ptr + 5); } return (dev_info & _DEV_NO_INHERIT) ? FD_CLOEXEC : 0; } case F_SETFD: { unsigned int flag; unsigned long entry_ptr = 0; /* shut up -Wall */ __dpmi_regs regs; va_start (ap, cmd); flag = va_arg(ap, int); va_end(ap); /* DOS only passes the first 20 handles to child programs. In addition, handles 19 and 18 will be closed by the stub of the child program (if it is a DJGPP program). */ if (fd >= 18) { if (flag & FD_CLOEXEC) return 0; else { errno = ENOSYS; return -1; } } /* Determine if the documented interface will allow twiddling with the inherit bit. If not, fallback to the undocumented one. */ if (inherit_bit_visible == -1) inherit_bit_visible = inherit_bit_test(fd, dev_info); if (!inherit_bit_visible) { entry_ptr = _get_sft_entry_ptr(fd); if (entry_ptr == 0) { /* The fd has already been validated, so reaching here means something is wrong with _get_sft_entry_ptr. */ return -1; } dev_info = _farpeekw(_dos_ds, entry_ptr + 5); } if (flag & FD_CLOEXEC) dev_info |= _DEV_NO_INHERIT; else dev_info &= ~(_DEV_NO_INHERIT); if (inherit_bit_visible) { regs.x.ax = 0x4401; regs.x.bx = fd; regs.x.dx = dev_info; __dpmi_int(0x21, ®s); } else _farpokew(_dos_ds, entry_ptr + 5, dev_info); return 0; } case F_GETFL: { return 0; } case F_SETFL: { unsigned char new_mode_bits; va_start (ap, cmd); new_mode_bits = va_arg(ap,int); va_end(ap); /* Allow removal of O_NONBLOCK, since DJGPP doesn't support it anyway. */ return (new_mode_bits == 0) ? 0 : -1; } case F_GETLK: case F_SETLK: case F_SETLKW: { struct flock *lock_req = NULL; /* shut up -Wall */ int ret = -1; off_t pos, cur_pos, lock_pos; off_t len; cur_pos = lseek(fd, 0, SEEK_CUR); va_start (ap, cmd); lock_req = va_arg(ap, struct flock *); va_end (ap); lock_pos = lseek (fd, lock_req->l_start, lock_req->l_whence); len = lock_req->l_len; /* If l_len is zero, then the lock is to be set from l_start until the end-of-file. */ if (len == 0) { len = filelength(fd) - cur_pos; /* This should probably be an error. */ if (len <= 0) len = 1; } lseek (fd, cur_pos, SEEK_SET); /* If l_len is positive, the area to lock is from l_start to l_start + l_len - 1. If l_len is negative, the area to lock is from l_start + len to l_start - 1. */ if (len > 0) { pos = lock_pos; } else { pos = lock_pos + len; len = -lock_req->l_len; } /* DOS/Windows only support read locks on a per-file basis, so any attempted use of a read lock is an error, UNLESS all of these conditions are true, which makes the request into a per-file lock equivalent to F_WRLCK: lock_req->l_whence = SEEK_SET lock_req->l_start == 0 lock_req->l_len == 0 */ if ((lock_req->l_type == F_RDLCK) && (lock_req->l_whence != SEEK_SET || lock_req->l_start != 0 || lock_req->l_len != 0)) { errno = ENOSYS; return -1; } if (lock_req->l_type == F_UNLCK) { ret = _dos_unlock(fd, pos, len); } else if ((lock_req->l_type == F_WRLCK) || (lock_req->l_type == F_RDLCK)) { ret = _dos_lock(fd, pos, len); if (cmd == F_GETLK) { if (ret == 0) { _dos_unlock(fd, pos, len); /* If no lock is found that would prevent a lock from being created, the lock type is set to F_UNLCK. */ lock_req->l_type = F_UNLCK; } else { /* If a lock is found then l_whence, l_start, and l_len should point to the area covered by the lock. But the file locking interface doesn't report where the existing lock is, so nothing more can be done. */ ; } } /* If F_SETLKW is set, wait for the lock to be released. */ if (cmd == F_SETLKW && ret < 0) while ((ret = _dos_lock(fd, pos, len)) < 0) __dpmi_yield(); } if (ret < 0) errno = ENOSYS; return ret; } } /* In case fcntl is called with an unrecognized command. */ errno = ENOSYS; return -1; } --=====================_55779254==_ Content-Type: text/plain; charset="us-ascii" Content-Disposition: attachment; filename="tfcntl.c" #include #include #include int main() { #if defined(F_SETLK) && defined(F_SETLKW) struct flock flock; int retval, fd; fd = open("tfcntl.c", O_RDONLY); flock.l_type = F_RDLCK; flock.l_whence = SEEK_SET; flock.l_start = flock.l_len = 0; retval = fcntl(fd, F_SETLK, &flock); printf("F_SETLK/F_RDLCK:retval=%d\n", retval); if (retval < 0) exit(2); retval = fcntl(fd, F_GETLK, &flock); printf("F_GETLK:retval=%d\n", retval); flock.l_type = F_UNLCK; retval = fcntl(fd, F_SETLK, &flock); printf("F_SETLK/F_UNLCK:retval=%d\n", retval); if (retval < 0) exit(3); retval = fcntl(fd, F_GETFD); printf("F_GETFD:retval=%04x\n", retval); retval = fcntl(fd, F_SETFL, O_BINARY); printf("F_SETFL:retval=%04x\n", retval); retval = fcntl(fd, F_GETFL); printf("F_GETFL:retval=%04x\n", retval); close(fd); if (retval < 0) exit(4); exit(0); #else exit(99); #endif } --=====================_55779254==_ Content-Type: text/plain; charset="us-ascii"; format=flowed --------------------------------------------------------- Peter J. Farley III (pjfarley AT dorsai DOT org OR pjfarley AT banet DOT net) --=====================_55779254==_--