summaryrefslogtreecommitdiff
path: root/libfshelp-tests
diff options
context:
space:
mode:
authorNeal H Walfield <neal@cs.uml.edu>2019-10-30 00:03:13 +0100
committerSamuel Thibault <samuel.thibault@ens-lyon.org>2019-10-30 00:04:40 +0100
commit3a8234d2440a464f5401b3444d454ac913d6b3d6 (patch)
tree409ff15b23279ea508c18fc4d1909da55fc9ca6e /libfshelp-tests
parent14639382938714f07f76abd3d221c6d472710502 (diff)
libfshelp-tests: Tests for file record locking
2019-03-04 Svante Signell <svante.signell@gmail.com> * test-*.c: Update code, remove test results. * README.new_tests: New file, summarize new test results. 2019-02-12 Svante Signell <svante.signell@gmail.com> * test-*.c: Update code, add some test results. * Makefile: Remove extra flags. 2019-02-01 Svante Signell <svante.signell@gmail.com> * Update copyright years. 2016-05-23 Svante Signell <svante.signell@gmail.com> * Makefile: Add sub-directory libfshelp-tests. 2018-12-07 Svante Signell <svante.signell@gmail.com> * Update copyright years. * locks.c(cmd_lock): Call fshelp_rlock_tweak() with new last argument rendezvous = MACH_PORT_NULL. 2017-01-05 Svante Signell <svante.signell@gmail.com> * Update copyright years and headers. 2016-12-28 Svante Signell <svante.signell@gmail.com> * Makefile: test-flock.c, test-lockf.c and test-fcntl.c * test-lockf.c: New file * Rename set-flock.c, set-fcntl.c to test-flock.c test-fcntl.c * TODO: Update README 2016-05-23 Svante Signell <svante.signell@gmail.com> * Makefile: Link with pthread, add build of set-flock.c and set-fcntl.c * define temporary CPP_FLAGS until glibc is updated * set-flock.c, set-fcntl.c: New files. * Fix typos in README 2001-04-11 Neal H Walfield <neal@cs.uml.edu> * ChangeLog: New file, mentioning itself in this sentence. * Makefile: New file. * README: Likewise. * fork.c: Likewise. * locks: Likewise. * locks-tests: Likewise. * locks.c: Likewise. * race.c: Likewise.
Diffstat (limited to 'libfshelp-tests')
-rw-r--r--libfshelp-tests/Makefile42
-rw-r--r--libfshelp-tests/README102
-rw-r--r--libfshelp-tests/README.new_tests146
-rw-r--r--libfshelp-tests/fork.c80
-rw-r--r--libfshelp-tests/locks-tests585
-rw-r--r--libfshelp-tests/locks.c328
-rw-r--r--libfshelp-tests/race.c83
-rw-r--r--libfshelp-tests/test-fcntl.c273
-rw-r--r--libfshelp-tests/test-flock.c170
-rw-r--r--libfshelp-tests/test-lockf.c184
10 files changed, 1993 insertions, 0 deletions
diff --git a/libfshelp-tests/Makefile b/libfshelp-tests/Makefile
new file mode 100644
index 00000000..3259016d
--- /dev/null
+++ b/libfshelp-tests/Makefile
@@ -0,0 +1,42 @@
+# Makefile libfshelp test cases
+#
+# Copyright (C) 2001, 2015-2019 Free Software Foundation, Inc.
+#
+# Written by Neal H Walfield <neal@cs.uml.edu>
+#
+# This file is part of the GNU Hurd.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>.
+
+dir := libfshelp-tests
+makemode := utilities
+
+targets = race locks fork test-flock test-lockf test-fcntl
+SRCS = race.c locks.c fork.c test-flock.c test-lockf.c test-fcntl.c
+
+MIGSTUBS = fsUser.o ioUser.o
+OBJS = $(SRCS:.c=.o) $(MIGSTUBS)
+HURDLIBS = fshelp ports
+LDLIBS += -lpthread
+
+race: race.o fsUser.o ioUser.o
+fork: fork.o fsUser.o
+locks: locks.o
+test-flock: test-flock.o
+test-lockf: test-lockf.o
+test-fcntl: test-fcntl.o ../libfshelp/libfshelp.a
+
+race locks: ../libfshelp/libfshelp.a ../libports/libports.a ../libshouldbeinlibc/libshouldbeinlibc.a
+
+include ../Makeconf
diff --git a/libfshelp-tests/README b/libfshelp-tests/README
new file mode 100644
index 00000000..3efc6a94
--- /dev/null
+++ b/libfshelp-tests/README
@@ -0,0 +1,102 @@
+These programs are used to help test the algorithms in the libfshelp
+library.
+
+Record Locking
+==============
+
+Race
+----
+
+Race locks a file, reads an integer, increments it, writes the result to
+the file and then unlocks the file -- 10,000 times. It is intended that
+multiple instances of this program be run at the same time. Race takes
+three arguments: the file to use, the start of the lock and the length.
+For obvious reasons, it is important that all instances of race have
+locks that overlap. For example:
+
+ # rm -f foo && ( ./race foo 2 0 & ./race foo 2 3 & \
+ > ./race foo 0 3 )
+ Was blocked 5482 times
+ Was blocked 5485 times
+ Was blocked 5479 times
+ # cat foo
+ 30000
+
+We see here that each process was blocked several thousand times and that
+the result in the file foo is 30000. Perfect.
+
+Locks
+-----
+
+Locks is an interactive shell that has one ``file'' and ten open file
+descriptors. Using some simple commands, one can test to see if locks
+are established, cleared, and enforced. The principal command is
+`lock,' which takes four parameters. The first is the file descriptor
+to lock, the second is the start of the lock, the third is the length of
+the lock (0 = until EOF) and the last is the type of lock to establish
+from the set {0: F_UNLCK, 1: F_RDLCK, 2: F_WRLCK}. Help on the other
+commands can be gotten using the `help' command.
+
+A small run:
+
+ # ./locks
+ > lock 0 10 0 1
+ 0: Start = 10; Length = 0; Type = F_RDLCK
+
+Lock from byte 10 through the EOF.
+
+ > lock 0 20 0 0
+ 0: Start = 10; Length = 10; Type = F_RDLCK
+
+Unlock from byte 20 through the EOF.
+
+ > lock 0 11 8 2
+ 0: Start = 10; Length = 1; Type = F_RDLCK
+ Start = 11; Length = 8; Type = F_WRLCK
+ Start = 19; Length = 1; Type = F_RDLCK
+
+Upgrade bytes 11 through 19 to a write lock.
+
+ > lock 0 9 10 1
+ 0: Start = 9; Length = 2; Type = F_RDLCK
+ Start = 11; Length = 8; Type = F_WRLCK
+ Start = 19; Length = 1; Type = F_RDLCK
+
+Add a read lock to byte 9.
+
+ > lock 1 0 10 1
+ 1: Start = 0; Length = 10; Type = F_RDLCK
+
+Read lock the first ten bytes of the file through file descriptor 1.
+
+ > lock 1 10 0 1
+ Resource temporarily unavailable
+
+Attempts to read lock the rest of the file. This, however, fails as
+there are outstanding write locks held through file descriptor 1.
+
+ > lock 1 10 0 0
+ 1: Start = 0; Length = 10; Type = F_RDLCK
+
+What happens when file descriptor tries to unlock the blocked range?
+
+ > lock 1 10 0 2
+ Resource temporarily unavailable
+
+Nothing.
+
+A bunch of tests live in locks-tests. One can run them through the test
+program using: `./locks < ./locks-test 2>&1 | less'. If it core dumps or
+triggers an assertion, that is a bug. Report it.
+
+Fork
+----
+
+Tests to see if the a child inherits the locks across a fork. According
+to POSIX, the child should not.
+
+ # ./fork foo
+ Parent has a write lock; Others have a write lock.
+ Child has a write lock; Others have a write lock.
+
+We are not POSIX compliant.
diff --git a/libfshelp-tests/README.new_tests b/libfshelp-tests/README.new_tests
new file mode 100644
index 00000000..b31b5034
--- /dev/null
+++ b/libfshelp-tests/README.new_tests
@@ -0,0 +1,146 @@
+File: README.new_tests
+
+C-code:
+=======
+test-fcntl.c
+test-lockf.c
+test-flock.c
+
+Compile:
+========
+gcc -g -Wall -D_FILE_OFFSET_BITS=64 -o test-fcntl test-fcntl.c
+gcc -g -Wall -D_FILE_OFFSET_BITS=64 -o test-lockf test-lockf.c
+gcc -g -Wall -D_FILE_OFFSET_BITS=64 -o test-flock test-flock.c
+
+./test-fcntl
+./test-fcntl: Usage: ./test-fcntl file [flags] [cmd] [len] [sleep_time]
+ file : file name/device name
+ flags : r (O_RDONLY) | w (O_WRONLY) | rw (O_RDWR) : [rw]
+ cmd : g (F_GETLK), s (F_SETLK), sw (F_SETLKW) : [s]
+ lock.l_type : rl (F_RDLCK), wl (F_WRLCK), ul [F_UNLCK] : [ul]
+ lock.l_whence : ss (SEEK_SET), sc (SEEK_CUR), se (SEEK_END) : [ss]
+ lock.l_start : b <number> : [b 0]
+ lock.l_len : l <number> : [l 0]
+ sleep_time : st <number> : [st 10]
+
+./test-lockf
+./test-lockf: Usage: ./test-lockf file [flags] [cmd] [len] [sleep_time]
+ file : file name/device name
+ flags : r (O_RDONLY) | w (O_WRONLY) | rw (O_RDWR) : [w]
+ cmd : x (F_LOCK), xt (F_TLOCK), u (F_ULOCK),
+ t (F_TEST) : [x]
+ len : l <number> : [0]
+ sleep_time : st <number> : [st 10]
+
+./test-flock
+./test-flock: Usage: ./test-flock file [flags] [operation] [sleep_time]
+ file : file name/device name
+ flags : r (O_RDONLY) | w (O_WRONLY) | rw (O_RDWR) : [w]
+ operation : s (LOCK_SH), x (LOCK_EX), u (LOCK_UN),
+ sn (LOCK_SH | LOCK_UN), xn (LOCK_EX | LOCK_UN) : [s]
+ sleep_time : st <number> : [st 10]
+
+Results:
+========
+New version of file_record_lock:
+libdiskfs/file-lock.c(diskfs_S_file_lock): Commented out
+libnetfs/file-lock.c(netfs_S_file_lock): Commented out
+libtrivfs/file-lock.c(trivfs_S_file_lock): TODO: Add+comment out
+libtrivfs/make-peropen.c(trivfs_S_file_lock): FIXME: po->openmodes = O_RDWR;
+if (openstat & (O_RDONLY|O_WRONLY|O_EXEC)) openstat |= O_RDONLY|O_WRONLY;
+
+libfshelp/rlock-tweak.c(fshelp_rlock_tweak): Removed
+if (lock->l_type == F_RDLCK && !(open_mode & O_READ))
+ return EACCES;
+if (lock->l_type == F_WRLCK && !(open_mode & O_WRITE))
+ return EACCES;
+Added:
+/* From POSIX-1003.1: A request for an exclusive lock shall fail if
+ the file descriptor was not opened with write access. */
+if ((cmd == F_SETLK64 || cmd == F_SETLKW64 )
+ && lock->l_type == F_WRLCK && !(open_mode & O_WRITE))
+ return EBADF;
+
+test-fcntl:
+===========
+libdiskfs:
+----------
+
+touch foo
+./test-fcntl foo {r,w} g {rl,wl}
+./test-fcntl foo r {s,sw} wl
+./test-fcntl: fcntl: Bad file descriptor
+./test-fcntl foo r {s,sw} {rl,ul}
+
+T1: T2:
+./test-fcntl foo {r,w} {s,sw} {rl,wl}
+./test-fcntl foo {r,w} {s,sw} {rl,wl}
+
+libnetfs:
+---------
+Not applicable on GNU/Linux?
+
+settrans -c ftp: /hurd/hostmux /hurd/ftpfs /
+Check:
+file ftp:
+ftp:: directory
+ls ftp://ftp.gnu.org/
+less ftp://ftp.gnu.org/README
+
+./test-fcntl ftp://ftp.gnu.org/README r g {rl,wl}
+./test-fcntl ftp://ftp.gnu.org/README w g {rl,wl}
+./test-fcntl: open: Permission denied
+
+./test-fcntl ftp://ftp.gnu.org/README r {s,sw} {rl,ul}
+./test-fcntl ftp://ftp.gnu.org/README r {s,sw} wl
+./test-fcntl: fcntl: Bad file descriptor
+
+settrans -fg ftp:
+rm ftp:
+
+libtrivs:
+---------
+BUG: ./test-fcntl /dev/null r s wl
+GNU/Linux:
+./test-fcntl: fcntl: Bad file descriptor
+
+test-lockf:
+===========
+libdiskfs:
+----------
+touch foo
+./test-lockf foo {r,w} {x,xt,u,t}
+T1, T2:
+./test-lockf foo {r,w} {x,xt,u,t}
+
+libnetfs:
+---------
+
+libtrivfs:
+----------
+
+test-flock:
+===========
+libdiskfs:
+----------
+touch foo
+./test-flock foo {r,w} {s,x,u,sn,xn}
+
+GNU/Linux:
+./test-flock foo r x
+
+GNU/Hurd:
+./test-flock foo r {x,xn}
+./test-flock: flock: Bad file descriptor
+
+T1: ./test-flock foo w s,x
+T2: ./test-flock foo w x (waits for the first lock)
+
+libnetfs:
+---------
+
+libtrivfs:
+----------
+GNU/Hurd
+T1: ./test-flock /dev/null w s,x
+BUG: T2: ./test-flock /dev/null w x (does not wait for the first lock)
diff --git a/libfshelp-tests/fork.c b/libfshelp-tests/fork.c
new file mode 100644
index 00000000..704e68b6
--- /dev/null
+++ b/libfshelp-tests/fork.c
@@ -0,0 +1,80 @@
+/* Test is a process inherits locks after a fork.
+
+ Copyright (C) 2001 Free Software Foundation, Inc.
+
+ Written by Neal H Walfield <neal@cs.uml.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <assert.h>
+#include <stdio.h>
+#include <error.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include "fs_U.h"
+#include <hurd.h>
+
+char *lock2str (int type)
+{
+ if (type & LOCK_SH)
+ return "read";
+ if (type & LOCK_EX)
+ return "write";
+ if (type & LOCK_UN)
+ return "unlocked";
+ assert (! "Invalid");
+ return NULL;
+}
+
+int main (int argc, char **argv)
+{
+ error_t err;
+ struct flock64 lock;
+ mach_port_t rendezvous = MACH_PORT_NULL;
+ int fd;
+ pid_t pid;
+ int mine, others;
+
+ if (argc != 2)
+ error (1, 0, "Usage: %s file", argv[0]);
+
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 0;
+ lock.l_type = F_WRLCK;
+
+ fd = file_name_lookup (argv[1], O_READ | O_WRITE | O_CREAT, 0666);
+ if (fd == MACH_PORT_NULL)
+ error (1, errno, "file_name_lookup");
+
+ err = file_record_lock (fd, F_SETLK64, &lock, rendezvous, MACH_MSG_TYPE_MAKE_SEND);
+ if (err)
+ error (1, err, "file_record_lock");
+
+ pid = fork ();
+ if (pid == -1)
+ error (1, errno, "fork");
+
+ err = file_lock_stat (fd, &mine, &others);
+ if (err)
+ error (1, err, "file_lock_stat");
+
+ printf ("%s has a %s lock; Others have a %s lock.\n",
+ pid ? "Parent" : "Child", lock2str (mine), lock2str (others));
+
+ mach_port_deallocate (mach_task_self (), fd);
+
+ return 0;
+}
diff --git a/libfshelp-tests/locks-tests b/libfshelp-tests/locks-tests
new file mode 100644
index 00000000..5ba5468c
--- /dev/null
+++ b/libfshelp-tests/locks-tests
@@ -0,0 +1,585 @@
+echo Legend:
+echo + => Shared region
+echo x => Exclusive region
+echo ' ' => Unlocked region
+echo . => Clearing region
+echo [] => Start/End of a region
+echo After each lock command, the proposed region is shown followed
+echo by the result of applying it.
+echo * Established region wraps new region
+echo ** Both boundaries align
+echo [
+exec lock 0 1 10 1
+echo [+++++++++++++]
+echo [ [+++++++++++++]
+exec lock 0 1 10 1
+echo [+++++++++++++]
+echo [ [+++++++++++++]
+exec lock 0 1 10 2
+echo [xxxxxxxxxxxxx]
+echo [ [xxxxxxxxxxxxx]
+exec lock 0 1 10 1
+echo [+++++++++++++]
+echo [ [xxxxxxxxxxxxx]
+exec lock 0 1 10 2
+echo [xxxxxxxxxxxxx]
+echo [ [xxxxxxxxxxxxx]
+exec lock 0 1 10 0
+echo [.............]
+echo [
+exec lock 0 10 0 1
+echo [++++++++
+echo [ [++++++++
+exec lock 0 10 0 1
+echo [++++++++
+echo [ [++++++++
+exec lock 0 10 0 2
+echo [xxxxxxxx
+echo [ [xxxxxxxx
+exec lock 0 10 0 2
+echo [xxxxxxxx
+echo [ [xxxxxxxx
+exec lock 0 10 0 1
+echo [++++++++
+echo [ [xxxxxxxx
+exec lock 0 10 0 0
+echo [........
+echo [
+echo ** Left boundaries align
+exec lock 0 1 10 1
+echo [+++++++++++]
+echo [ [+++++++++++]
+exec lock 0 1 5 1
+echo [+++++]
+echo [ [+++++++++++]
+exec lock 0 1 5 2
+echo [xxxxx]
+echo [ [xxxxx][++++]
+exec lock 0 1 3 1
+echo [++]
+echo [ [xxxxx][++++]
+exec lock 0 1 10 0
+echo [............
+echo [
+exec lock 0 1 10 1
+echo [+++++++++++]
+echo [ [+++++++++++]
+exec lock 0 1 9 2
+echo [xxxxxxxx]
+echo [ [xxxxxxxx][+]
+exec lock 0 1 5 0
+echo [...]
+echo [ [***][+]
+exec lock 0 6 3 0
+echo [.]
+echo [ [*][+]
+exec lock 0 9 1 0
+echo [.]
+echo [ [+]
+exec lock 0 10 1 0
+echo [.]
+echo [
+exec lock 0 1 0 1
+echo [++++++++++++
+echo [ [++++++++++++
+exec lock 0 1 5 1
+echo [++++]
+echo [ [++++++++++++
+exec lock 0 1 5 2
+echo [xxxx]
+echo [ [xxxx][++++++
+exec lock 0 1 0 0
+echo [............
+echo [
+exec lock 0 5 0 2
+echo [xxxxxxxxxx
+echo [ [xxxxxxxxxx
+exec lock 0 5 5 1
+echo [++++]
+echo [ [xxxxxxxxxx
+exec lock 0 5 0 1
+echo [++++++++++
+echo [ [xxxxxxxxxx
+exec lock 0 0 0 0
+echo [..............
+echo [
+echo ** Common right side
+exec lock 0 5 5 1
+echo [+++++]
+echo [ [+++++]
+exec lock 0 9 1 1
+echo [+]
+echo [ [+++++]
+exec lock 0 9 1 2
+echo [x]
+echo [ [++][x]
+exec lock 0 5 4 2
+echo [xx]
+echo [ [xxxxx]
+exec lock 0 8 2 2
+echo [x]
+echo [ [xxxxx]
+exec lock 0 9 1 0
+echo [.]
+echo [ [xxx]
+exec lock 0 5 4 0
+echo [...]
+echo [
+exec lock 0 5 0 1
+echo [++++++++++
+echo [ [++++++++++
+exec lock 0 10 0 1
+echo [+++++
+echo [ [++++++++++
+exec lock 0 10 0 2
+echo [xxxxx
+echo [ [+++][xxxxx
+exec lock 0 5 0 0
+echo [..........
+echo [
+echo ** Completely interior
+exec lock 0 5 10 1
+echo [++++++++]
+echo [ [++++++++]
+exec lock 0 6 8 1
+echo [++++++]
+echo [ [++++++++]
+exec lock 0 8 3 1
+echo [+]
+echo [ [++++++++]
+exec lock 0 8 3 2
+echo [x]
+echo [ [+][x][++]
+exec lock 0 12 1 2
+echo x
+echo [ [++xxx+x+]
+exec lock 0 6 8 0
+echo [......]
+echo [ [+] [+]
+exec lock 0 0 0 0
+echo [..............
+echo [
+exec lock 0 5 0 1
+echo [+++++++++
+echo [ [+++++++++
+exec lock 0 10 0 1
+echo [++++
+echo [ [+++++++++
+exec lock 0 10 0 2
+echo [xxxx
+echo [ [+++][xxxx
+exec lock 0 11 0 0
+echo [..
+echo [ [+++][x]
+exec lock 0 6 0 0
+echo [.......
+echo [ [+]
+exec lock 0 5 0 0
+echo [.........
+echo [
+exec lock 0 10 0 1
+echo [+++++
+echo [ [+++++
+exec lock 0 11 0 1
+echo [+]
+echo [ [+++++
+exec lock 0 11 0 1
+echo [+]
+echo [ [+++++
+exec lock 0 11 1 2
+echo [x]
+echo [ [+x+++
+exec lock 0 13 0 1
+echo [+]
+echo [ [+x+++
+exec lock 0 10 0 0
+echo [.....
+echo [
+echo * We wrap the locked region
+echo ** Left boundaries align
+exec lock 0 1 10 1
+echo [++++]
+echo [[++++]
+exec lock 0 1 15 1
+echo [++++++++]
+echo [[++++++++]
+exec lock 0 1 16 1
+echo [+++++++++]
+echo [[+++++++++]
+exec lock 0 1 20 2
+echo [xxxxxxxxxxxxx]
+echo [[xxxxxxxxxxxxx]
+exec lock 0 1 30 1
+echo [+++++++++++++++++++]
+echo [[xxxxxxxxxxxxx][++++]
+exec lock 0 22 11 2
+echo [xxx]
+echo [[xxxxxxxxxxxxx][+][xx]
+exec lock 0 1 40 0
+echo [.........................]
+echo [
+exec lock 0 1 0 1
+echo [++++++++++
+echo [[++++++++++
+exec lock 0 1 0 1
+echo [++++++++++
+echo [[++++++++++
+exec lock 0 1 0 2
+echo [xxxxxxxxxx
+echo [[xxxxxxxxxx
+exec lock 0 0 0 0
+echo [...........
+echo [
+exec lock 0 1 0 1
+echo [++++++++++
+echo [[++++++++++
+exec lock 0 10 0 1
+echo [+++++
+echo [[++++++++++
+exec lock 0 10 0 2
+echo [xxxxx
+echo [[+++][xxxxx
+exec lock 0 0 0 0
+echo [...........
+echo [
+echo ** Right boundaries align
+exec lock 0 5 10 1
+echo [++++++++]
+echo [ [++++++++]
+exec lock 0 4 11 1
+echo [+++++++++]
+echo [ [+++++++++]
+exec lock 0 3 12 2
+echo [xxxxxxxxxx]
+echo [ [xxxxxxxxxx]
+exec lock 0 0 15 2
+echo [xxxxxxxxxxxx]
+echo [xxxxxxxxxxxx]
+exec lock 0 0 0 0
+echo [.............
+echo [
+exec lock 0 5 0 1
+echo [++++++++
+echo [ [++++++++
+exec lock 0 4 0 1
+echo [+++++++++
+echo [ [+++++++++
+exec lock 0 3 0 2
+echo [xxxxxxxxxx
+echo [ [xxxxxxxxxx
+exec lock 0 2 0 2
+echo [xxxxxxxxxxx
+echo [ [xxxxxxxxxxx
+exec lock 0 0 0 2
+echo [xxxxxxxxxxxxx
+echo [xxxxxxxxxxxxx
+exec lock 0 0 0 0
+echo [.............
+echo [
+echo ** Consume locked region
+exec lock 0 5 10 1
+echo [++++++++]
+echo [ [++++++++]
+exec lock 0 4 12 1
+echo [++++++++++]
+echo [ [++++++++++]
+exec lock 0 2 16 1
+echo [++++++++++++++]
+echo [ [++++++++++++++]
+exec lock 0 1 18 2
+echo [xxxxxxxxxxxxxxxx]
+echo [[xxxxxxxxxxxxxxxx]
+exec lock 0 0 24 2
+echo [xxxxxxxxxxxxxxxxxxxxx]
+echo [xxxxxxxxxxxxxxxxxxxxx]
+exec lock 0 0 30 0
+echo [.........................]
+echo [
+exec lock 0 5 3 1
+echo [++]
+echo [ [++]
+exec lock 0 10 5 1
+echo [++++]
+echo [ [++] [++++]
+exec lock 0 20 5 2
+echo [xxxx]
+echo [ [++] [++++] [xxxx]
+exec lock 0 4 30 2
+echo [xxxxxxxxxxxxxxxxxxxxxxxxxx]
+echo [ [xxxxxxxxxxxxxxxxxxxxxxxxxx]
+exec lock 0 1 35 1
+echo [++++++++++++++++++++++++++++++]
+echo [ [+[xxxxxxxxxxxxxxxxxxxxxxxxxx]+]
+exec lock 0 0 40 1
+echo [+++++++++++++++++++++++++++++++++++]
+echo [++][xxxxxxxxxxxxxxxxxxxxxxxxxx][+++]
+exec lock 0 0 0 0
+echo [....................................
+echo [
+exec lock 0 5 5 1
+echo [+++]
+echo [ [+++]
+exec lock 0 4 0 1
+echo [++++++]
+echo [ [++++++]
+exec lock 0 4 0 0
+echo [..........
+echo [
+exec lock 0 5 3 1
+echo [++]
+echo [ [++]
+exec lock 0 10 5 1
+echo [++++]
+echo [ [++] [++++]
+exec lock 0 0 0 1
+echo [++++++++++++++++++++
+echo [++++++++++++++++++++
+exec lock 0 0 0 0
+echo [....................
+echo [
+exec lock 0 5 3 1
+echo [++]
+echo [ [++]
+exec lock 0 10 3 1
+echo [++]
+echo [ [++] [++]
+exec lock 0 4 0 1
+echo [++++++++++++++
+echo [ [++++++++++++++
+exec lock 0 10 3 2
+echo [**]
+echo [ [++++++][**][++
+exec lock 0 0 0 2
+echo [xxxxxxxxxxxxxxxxxx
+echo [xxxxxxxxxxxxxxxxxx
+exec lock 0 0 0 0
+echo [..................
+echo [
+echo * Our start is within the locked region or one byte after and our
+echo end is after the end of the locked region.
+echo ** The regions are the same type: Merge into a single large region
+exec lock 0 5 5 1
+echo [+++]
+echo [ [+++]
+exec lock 0 6 5 1
+echo [+++]
+echo [ [++++]
+exec lock 0 8 8 1
+echo [++++++]
+echo [ [+++++++++]
+exec lock 0 16 4 1
+echo [++]
+echo [ [+++++++++++++]
+exec lock 0 20 0 1
+echo [+++
+echo [ [++++++++++++++++++
+exec lock 0 5 16 0
+echo [..............]
+echo [ [+++
+exec lock 0 20 0 0
+echo [...
+echo [
+exec lock 0 6 6 2
+echo [xxxx]
+echo [ [xxxx]
+exec lock 0 7 7 2
+echo [xxxxx]
+echo [ [xxxxxx]
+exec lock 0 14 7 2
+echo [xxxxx]
+echo [ [xxxxxxxxxxxxx]
+exec lock 0 21 0 2
+echo [xx
+echo [ [xxxxxxxxxxxxxxxxx
+exec lock 0 6 0 0
+echo [.................
+echo [
+echo ** Different types just after the end of the locked region
+exec lock 0 1 3 1
+echo [++]
+echo [[++]
+exec lock 0 4 3 2
+echo [xx]
+echo [[++][xx]
+exec lock 0 7 3 1
+echo [++]
+echo [[++][xx][++]
+exec lock 0 10 0 2
+echo [xxx
+echo [[++][xx][++][xxx
+exec lock 0 5 0 0
+echo [.........
+echo [[++]x
+exec lock 0 5 0 1
+echo [+++
+echo [[++]x[+++
+exec lock 0 1 0 0
+echo [...
+echo [
+echo ** New region consumes the intersection.
+exec lock 0 5 5 1
+echo [+++]
+echo [ [+++]
+exec lock 0 8 6 2
+echo [xxx]
+echo [ [++][xxx]
+exec lock 0 6 0 2
+echo [xxxxxx
+echo [ [+][xxxxxx
+exec lock 0 5 0 0
+echo [.........
+echo [
+echo ** New region is dominated
+exec lock 0 5 5 2
+echo [xxx]
+echo [ [xxx]
+exec lock 0 8 6 1
+echo [++++]
+echo [ [xxx][++]
+exec lock 0 6 0 1
+echo [++++++
+echo [ [xxx][+++
+exec lock 0 5 0 0
+echo [........
+echo [
+echo * Our start falls before the locked region. Our end falls just
+echo before or with in the region (although we do not consume it)
+echo ** The regions are the same type: Merge into a single large region
+exec lock 0 10 5 1
+echo [+++]
+echo [ [+++]
+exec lock 0 5 5 1
+echo [+++]
+echo [ [++++++++]
+exec lock 0 4 4 1
+echo [++]
+echo [ [+++++++++]
+exec lock 0 0 10 1
+echo [+++++++++]
+echo [+++++++++++++]
+exec lock 0 0 15 0
+echo [...............]
+echo [
+exec lock 0 10 0 1
+echo [++++
+echo [ [++++
+exec lock 0 5 5 1
+echo [+++]
+echo [ [+++++++++
+exec lock 0 4 1 1
+echo +
+echo [ [++++++++++
+exec lock 0 0 0 1
+echo [++++++++++++++
+echo [++++++++++++++
+exec lock 0 0 0 0
+echo [....
+echo [
+exec lock 0 10 5 2
+echo [xxx]
+echo [ [xxx]
+exec lock 0 5 5 2
+echo [xxx]
+echo [ [xxxxxxxx]
+exec lock 0 4 4 2
+echo [xx]
+echo [ [xxxxxxxxx]
+exec lock 0 0 10 2
+echo [xxxxxxxxx]
+echo [xxxxxxxxxxxxx]
+exec lock 0 0 15 0
+echo [...............]
+echo [
+exec lock 0 10 0 2
+echo [xxxx
+echo [ [xxxx
+exec lock 0 5 5 2
+echo [xxx]
+echo [ [xxxxxxxxx
+exec lock 0 4 1 2
+echo x
+echo [ [xxxxxxxxxx
+exec lock 0 0 0 2
+echo [xxxxxxxxxxxxxx
+echo [xxxxxxxxxxxxxx
+exec lock 0 0 0 0
+echo [....
+echo [
+echo ** Different types just before the start of the locked region
+exec lock 0 5 5 1
+echo [+++]
+echo [ [+++]
+exec lock 0 3 2 2
+echo [x]
+echo [ [x][+++]
+exec lock 0 2 1 1
+echo +
+echo [ +[x][+++]
+exec lock 0 1 0 2
+echo [xxxx
+echo [[xxxx
+exec lock 0 0 1 1
+echo +
+echo +[xxxx
+exec lock 0 0 0 0
+echo [....
+echo [....
+exec lock 0 5 0 1
+echo [++++
+echo [ [++++
+exec lock 0 0 5 2
+echo [xxxxxxxxx
+echo [xxxxxxxxx
+exec lock 0 0 0 0
+echo [....
+echo [
+echo ** New region consumes the intersection.
+exec lock 0 5 5 1
+echo [+++]
+echo [ [+++]
+exec lock 0 4 3 2
+echo [x]
+echo [ [x][+]
+exec lock 0 2 0 2
+echo [xxxxxx
+echo [ [xxxxxx
+exec lock 0 0 0 0
+echo [....
+echo [
+exec lock 0 5 0 1
+echo [++++++
+echo [ [++++++
+exec lock 0 4 5 2
+echo [xxx]
+echo [ [xxx][++
+exec lock 0 4 0 0
+echo [....
+echo [
+exec lock 0 5 0 1
+echo [++++
+echo [ [++++
+exec lock 0 0 0 2
+echo [xxxxxxxxxx
+echo [xxxxxxxxxx
+exec lock 0 0 0 0
+echo [....
+echo [
+echo ** New region is dominated
+exec lock 0 5 5 2
+echo [xxx]
+echo [ [xxx]
+exec lock 0 4 5 1
+echo [+++]
+echo [ +[xxx]
+exec lock 0 0 0 0
+echo [...
+echo [
+exec lock 0 5 0 2
+echo [xxxx
+echo [ [xxxx
+exec lock 0 0 0 1
+echo [++++++++
+echo [+++[xxxx
+exec lock 0 0 0 0
+echo [....
+echo [
diff --git a/libfshelp-tests/locks.c b/libfshelp-tests/locks.c
new file mode 100644
index 00000000..d9af78ae
--- /dev/null
+++ b/libfshelp-tests/locks.c
@@ -0,0 +1,328 @@
+/* Test record locking.
+
+ Copyright (C) 2001, 2018-2019 Free Software Foundation, Inc.
+
+ Written by Neal H Walfield <neal@cs.uml.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "../libfshelp/fshelp.h"
+#include "../libfshelp/rlock.h"
+#include <errno.h>
+#include <error.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "fs_U.h"
+
+#ifndef PEROPENS
+#define PEROPENS 10
+#endif
+
+struct rlock_box box;
+struct rlock_peropen peropens[PEROPENS];
+loff_t pointers[PEROPENS];
+loff_t file_size;
+
+struct command
+{
+ char *name;
+ int (*func)(char *cmds);
+ char *doc;
+};
+
+error_t cmd_help (char *);
+error_t cmd_comment (char *);
+error_t cmd_echo (char *);
+error_t cmd_lock (char *);
+error_t cmd_list (char *);
+error_t cmd_seek (char *);
+error_t cmd_exec (char *);
+
+struct command commands [] =
+ {
+ { "help", cmd_help, "Print this screen" },
+ { "#", cmd_comment, "Comment (Must _start_ the line)." },
+ { "echo", cmd_echo, "Echo the line." },
+ { "lock", cmd_lock,
+ "po start length type\n"
+ "\ttype = { F_UNLCK=0, F_RDLCK,=1, F_WRLCK=2 }" },
+ { "list", cmd_list, "list all locks' status" },
+ { "seek", cmd_seek, "PO1 ... Print the position of the given po.\n"
+ "\tPO1=N ... Seek a given po." },
+ { "exec", cmd_exec, "Execute a built in echoing the command."}
+ };
+
+error_t
+cmd_help (char *args)
+{
+ int i;
+ printf ("Commands:\n");
+ for (i = 0; i < sizeof (commands) / sizeof (struct command); i ++)
+ printf ("%s\t%s\n", commands[i].name, commands[i].doc);
+ return 0;
+}
+
+error_t
+cmd_comment (char *args)
+{
+ return 0;
+}
+
+error_t
+cmd_echo (char *args)
+{
+ printf ("%s", args);
+ return 0;
+}
+
+error_t
+cmd_lock (char *args)
+{
+ int po, type;
+ loff_t start, len;
+ struct flock64 lock;
+ mach_port_t rendezvous = MACH_PORT_NULL;
+ error_t err;
+
+ if (4 != sscanf (args, "%d %ld %ld %d", &po, (long*)&start, (long*)&len, &type))
+ {
+ printf ("Syntax error.\n");
+ return 0;
+ }
+
+ lock.l_type = type;
+ lock.l_whence = SEEK_CUR;
+ lock.l_start = (long)start;
+ lock.l_len = (long)len;
+
+ if (po < 0 || po >= PEROPENS)
+ {
+ printf ("Unknown peropen: %d.\n", po);
+ return 0;
+ }
+
+ switch (type)
+ {
+ case 0: lock.l_type = F_UNLCK; break;
+ case 1: lock.l_type = F_RDLCK; break;
+ case 2: lock.l_type = F_WRLCK; break;
+ default: printf ("Unknown type.\n"); return 0;
+ }
+
+ err= fshelp_rlock_tweak (&box, NULL, &peropens[po], O_RDWR,
+ file_size, pointers[po], F_SETLK64,
+ &lock, rendezvous);
+ if (! err)
+ {
+ char buf[10];
+ sprintf (buf, "%d\n", po);
+ cmd_list (buf);
+ }
+ return err;
+}
+
+error_t
+cmd_list (char *args)
+{
+ char *end;
+
+ void dump (int i)
+ {
+ struct rlock_list *l;
+
+ printf ("%3d:", i);
+ for (l = *peropens[i].locks; l; l = l->po.next)
+ {
+ printf ("\tStart = %4ld; Length = %4ld; Type = ", (long)l->start, (long)l->len);
+ switch (l->type)
+ {
+ case F_RDLCK: printf ("F_RDLCK"); break;
+ case F_WRLCK: printf ("F_WRLCK"); break;
+ case F_UNLCK: printf ("F_UNLCK"); break;
+ default: printf ("UNKNOWN"); break;
+ }
+ printf ("\n");
+ }
+
+ if (*peropens[i].locks == NULL)
+ printf ("\n");
+ }
+
+ while (*args == ' ')
+ args ++;
+
+ if (*args == '\n' || *args == '\0')
+ {
+ int i;
+
+ for (i = 0; i < PEROPENS; i ++)
+ dump (i);
+ return 0;
+ }
+
+ while (1)
+ {
+ long int p = strtoll (args, &end, 0);
+ if (end == args)
+ {
+ printf ("Syntax error.\n");
+ return 0;
+ }
+
+ if (p < 0 || p > PEROPENS)
+ printf ("%3ld:\tOut of range.", p);
+ else
+ dump (p);
+
+ while (*end == ' ')
+ end ++;
+
+ if (*end == '\n' || *end == '\0')
+ return 0;
+ args = end;
+ }
+}
+
+error_t
+cmd_seek (char *args)
+{
+ char *end;
+ int p;
+
+ while (*args == ' ')
+ args ++;
+
+ if (*args == '\n' || *args == '\0')
+ {
+ int i;
+ for (i = 0; i < PEROPENS; i ++)
+ printf ("%3d: %ld\n", i, (long)pointers[i]);
+ return 0;
+ }
+
+ while (1)
+ {
+ int set = 0;
+ long seek_to = 0;
+
+ p = strtol (args, &end, 0);
+ if (end == args)
+ {
+ printf ("Syntax error.\n");
+ return 0;
+ }
+
+ if (*end == '=')
+ {
+ set = 1;
+ args = end + 1;
+ seek_to = strtol (args, &end, 0);
+ if (end == args)
+ {
+ printf ("Syntax error.\n");
+ return 0;
+ }
+ }
+
+ if (p < 0 || p > PEROPENS)
+ printf ("%3d: unknown peropen\n", p);
+ else
+ {
+ printf ("%3d: %ld", p, (long)pointers[p]);
+ if (set)
+ printf (" => %ld\n", (long)(pointers[p] = seek_to));
+ else
+ printf ("\n");
+ }
+
+ while (*end == ' ')
+ end ++;
+ if (*end == '\0' || *end == '\n')
+ return 0;
+ args = end;
+ }
+}
+
+error_t
+interpret (char *buffer)
+{
+ int i;
+
+ while (*buffer == ' ')
+ buffer ++;
+
+ if (*buffer == '\n')
+ return 0;
+
+ for (i = 0; i < sizeof (commands) / sizeof (struct command); i ++)
+ if (strncmp (commands[i].name, buffer, strlen (commands[i].name)) == 0)
+ {
+ error_t err;
+ err = commands[i].func (buffer + strlen (commands[i].name) + 1);
+ if (err)
+ printf ("%s\n", strerror (err));
+ return err;
+ }
+
+ printf ("Unknown command.\n");
+ return 0;
+}
+
+error_t
+cmd_exec (char *arg)
+{
+ printf ("%s", arg);
+ interpret (arg);
+ return 0;
+}
+
+int main (int argc, char *argv[])
+{
+ int i;
+
+ if (argc != 1)
+ {
+ printf ("Usage: %s\n"
+ "\tType `help' at the prompt.\n"
+ "\tUsed to test the record locking functions in libfshelp\n",
+ argv[0]);
+ return 1;
+ }
+
+ fshelp_rlock_init (&box);
+ for (i = 0; i < PEROPENS; i ++)
+ fshelp_rlock_po_init (&peropens[i]);
+
+ while (! feof (stdin))
+ {
+ char b[1024];
+
+ printf ("> ");
+ fflush (stdout);
+
+ if (! fgets (b, sizeof (b), stdin))
+ {
+ if (feof (stdin))
+ break;
+ else
+ continue;
+ }
+
+ interpret (b);
+ }
+
+ printf ("\n");
+ return 0;
+}
diff --git a/libfshelp-tests/race.c b/libfshelp-tests/race.c
new file mode 100644
index 00000000..a1d398ab
--- /dev/null
+++ b/libfshelp-tests/race.c
@@ -0,0 +1,83 @@
+/* Test races in the record locking code.
+
+ Copyright (C) 2001 Free Software Foundation, Inc.
+
+ Written by Neal H Walfield <neal@cs.uml.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <error.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "fs_U.h"
+#include <hurd.h>
+#include "io_U.h"
+
+int main (int argc, char **argv)
+{
+ error_t err;
+ struct flock64 lock;
+ mach_port_t rendezvous = MACH_PORT_NULL;
+ int fd;
+ int i;
+ uint v;
+ int blocked = 0;
+ char buf[10] = "";
+ char *bufp;
+
+ if (argc != 4)
+ error (1, 0, "Usage: %s file start len", argv[0]);
+
+ lock.l_whence = SEEK_SET;
+ lock.l_start = atoi (argv[2]);
+ lock.l_len = atoi (argv[3]);
+
+ fd = file_name_lookup (argv[1], O_READ | O_WRITE | O_CREAT, 0666);
+ if (fd == MACH_PORT_NULL)
+ error (1, errno, "file_name_lookup");
+
+ for (i = 0; i < 10000; i ++)
+ {
+ lock.l_type = F_WRLCK;
+ err = file_record_lock (fd, F_SETLK64, &lock, rendezvous, MACH_MSG_TYPE_MAKE_SEND);
+ if (err)
+ {
+ blocked ++;
+ err = file_record_lock (fd, F_SETLKW64, &lock, rendezvous, MACH_MSG_TYPE_MAKE_SEND);
+ }
+ if (err)
+ error (1, err, "file_record_lock");
+
+ v = sizeof (buf);
+ bufp = buf;
+ io_read (fd, &bufp, &v, 0, v);
+
+ v = atoi (bufp);
+ sprintf (buf, "%d\n", v + 1);
+
+ v = 10;
+ io_write (fd, buf, sizeof (buf), 0, &v);
+ if (v == 0)
+ error (1, errno, "write (%d)", i);
+
+ lock.l_type = F_UNLCK;
+ file_record_lock (fd, F_SETLK64, &lock, rendezvous, MACH_MSG_TYPE_MAKE_SEND);
+ }
+
+ mach_port_deallocate (mach_task_self (), fd);
+
+ printf ("Was blocked %d times\n", blocked);
+ return 0;
+}
diff --git a/libfshelp-tests/test-fcntl.c b/libfshelp-tests/test-fcntl.c
new file mode 100644
index 00000000..7495e7ab
--- /dev/null
+++ b/libfshelp-tests/test-fcntl.c
@@ -0,0 +1,273 @@
+/* test-fcntl.c: Test advisory open file record locks, see fcntl(2)
+ Options: <see below>
+
+ Copyright (C) 2016-2019 Free Software Foundation, Inc.
+
+ Written by Svante Signell <svante.signell@gmail.com>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <error.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/file.h>
+
+/* Parse args */
+int parse_args (int argc, char **argv, char **file_name,
+ int *flags, char **flagsc,
+ int *cmd, char **cmdc,
+ struct flock *lock,
+ char **l_typec, char **l_whencec,
+ int *sleep_time)
+{
+ int i, tmp;
+ char *str, *endptr;
+
+ if (argc < 2)
+ error (1, 0, "Usage: %s file [flags] [cmd] [len] [sleep_time]\n\
+ file : file name/device name\n\
+ flags : r (O_RDONLY) | w (O_WRONLY) | rw (O_RDWR) : [rw]\n\
+ cmd : g (F_GETLK), s (F_SETLK), sw (F_SETLKW) : [s]\n\
+ lock.l_type : rl (F_RDLCK), wl (F_WRLCK), ul [F_UNLCK] : [ul]\n\
+ lock.l_whence : ss (SEEK_SET), sc (SEEK_CUR), se (SEEK_END) : [ss]\n\
+ lock.l_start : b <number> : [b 0]\n\
+ lock.l_len : l <number> : [l 0]\n\
+ sleep_time : st <number> : [st 10]\n",
+ argv[0]);
+
+ *file_name = argv[1];
+ for (i = 2; i < argc; i++)
+ {
+ str = argv[i];
+ if (strncmp (str, "r", 2) == 0)
+ {
+ *flags = O_RDONLY;
+ *flagsc = "O_RDONLY";
+ continue;
+ }
+ if (strncmp (str, "w", 2) == 0)
+ {
+ *flags = O_WRONLY;
+ *flagsc = "O_WRONLY";
+ continue;
+ }
+ if (strncmp (str, "rw", 2) == 0)
+ {
+ *flags = O_RDWR;
+ *flagsc = "O_RDWR";
+ continue;
+ }
+ if (strncmp (str, "s", 2) == 0)
+ {
+ *cmd = F_SETLK;
+ *cmdc = "F_SETLK";
+ continue;
+ }
+ if (strncmp (str, "sw", 2) == 0)
+ {
+ *cmd = F_SETLKW;
+ *cmdc = "F_SETLKW";
+ continue;
+ }
+ if (strncmp (str, "g", 2) == 0)
+ {
+ *cmd = F_GETLK;
+ *cmdc = "F_GETLK";
+ continue;
+ }
+ if (strncmp (str, "rl", 2) == 0)
+ {
+ lock->l_type = F_RDLCK;
+ *l_typec = "F_RDLCK";
+ continue;
+ }
+ if (strncmp (str, "wl", 2) == 0)
+ {
+ lock->l_type = F_WRLCK;
+ *l_typec = "F_WRLCK";
+ continue;
+ }
+ if (strncmp (str, "ul", 2) == 0)
+ {
+ lock->l_type = F_UNLCK;
+ *l_typec = "F_UNLCK";
+ continue;
+ }
+ if (strncmp (str, "ss", 2) == 0)
+ {
+ lock->l_whence = SEEK_SET;
+ *l_whencec = "SEEK_SET";
+ continue;
+ }
+ if (strncmp (str, "sc", 2) == 0)
+ {
+ lock->l_whence = SEEK_CUR;
+ *l_whencec = "SEEK_CUR";
+ continue;
+ }
+ if (strncmp (str, "se", 2) == 0)
+ {
+ lock->l_whence = SEEK_END;
+ *l_whencec = "SEEK_END";
+ continue;
+ }
+ if (strncmp (str, "b", 2) == 0)
+ {
+ str = argv[++i];
+ if (str)
+ {
+ errno = 0;
+ tmp = strtol (str, &endptr, 10);
+ if (tmp == 0 && errno != 0)
+ error (1, errno, "%s", str);
+ if (endptr == str)
+ error (1, EINVAL, "%s", str);
+ lock->l_start = tmp;
+ }
+ else
+ error (1, EINVAL, "%s", str);
+ continue;
+ }
+ if (strncmp (str, "l", 2) == 0)
+ {
+ str = argv[++i];
+ if (str)
+ {
+ errno = 0;
+ tmp = strtol (str, &endptr, 10);
+ if (tmp == 0 && errno != 0)
+ error (1, errno, "%s", str);
+ if (endptr == str)
+ error (1, EINVAL, "%s", str);
+ lock->l_len = tmp;
+ }
+ else
+ error (1, EINVAL, "%s", str);
+ continue;
+ }
+ if (strncmp (str, "st", 2) == 0)
+ {
+ str = argv[++i];
+ if (str)
+ {
+ errno = 0;
+ tmp = strtol (str, &endptr, 10);
+ if (tmp == 0 && errno != 0)
+ error (1, errno, "%s", str);
+ if (endptr == str)
+ error (1, EINVAL, "%s", str);
+ *sleep_time = tmp;
+ }
+ else
+ error (1, EINVAL, "%s", str);
+ continue;
+ }
+ error (1, EINVAL, "%s", str);
+ }
+
+ return 0;
+}
+
+int main (int argc, char **argv)
+{
+#ifdef __GNU__
+ error_t err;
+#else
+ int err;
+#endif
+ int fd, ret = -1;
+ char *file_name = NULL;
+ int flags = O_RDWR;
+ char *flagsc = "O_RDWR";
+ char *old_l_typec;
+ int old_l_type, old_l_pid;
+ int cmd = F_SETLK;
+ char *cmdc = "F_SETLK";
+ struct flock lock = {
+ F_UNLCK,
+ SEEK_SET,
+ 0,
+ 0,
+ 123456};
+ char *l_typec = "F_UNLCK";
+ char *l_whencec = "SEEK_SET";
+ int sleep_time = 10;
+
+ ret = parse_args (argc, argv, &file_name,
+ &flags, &flagsc,
+ &cmd, &cmdc,
+ &lock,
+ &l_typec, &l_whencec,
+ &sleep_time);
+
+#ifdef __GNU__
+ printf ("test-fcntl: GNU/Hurd\n");
+#else
+ printf ("test-fcntl: GNU/Linux\n");
+#endif
+ printf ("test-fcntl: [PID]=%d\n", getpid());
+ printf ("file = '%s', flags = %s\n", file_name, flagsc);
+ fd = open (file_name, flags);
+ if (fd < 0)
+ error (1, errno, "open");
+ printf ("Opening '%s', fd = %d, ", file_name, fd);
+ printf ("cmd = %s\n ", cmdc);
+ printf("lock = {l_type, l_whence, l_start, l_len, l_pid} =\n");
+#ifdef __GNU__
+ printf (" {%s, %s, %lld, %lld, %d}\n",
+#else
+ printf (" {%s, %s, %ld, %ld, %d}\n",
+#endif
+ l_typec, l_whencec, lock.l_start, lock.l_len, lock.l_pid);
+
+ old_l_type = lock.l_type;
+ old_l_typec = l_typec;
+ old_l_pid = lock.l_pid;
+
+ printf ("Requesting lock\n");
+ err = fcntl (fd, cmd, &lock);
+ if (err)
+ error (1, errno, "fcntl");
+
+ if (old_l_type != lock.l_type)
+ if (lock.l_type == F_UNLCK)
+ {
+ l_typec = "F_UNLCK";
+ printf("[PID=%ld] Lock can be placed\n", (long) getpid());
+ printf ("old_l_type = %s, l_type = %s\n", old_l_typec, l_typec);
+ return ret;
+ }
+ if (old_l_pid != lock.l_pid)
+ {
+ printf("[PID=%ld] Denied by %s lock on %lld:%lld "
+ "(held by PID %ld)\n", (long) getpid(),
+ (lock.l_type == F_RDLCK) ? "READ" : "WRITE",
+ (long long) lock.l_start,
+ (long long) lock.l_len, (long) lock.l_pid);
+ return ret;
+ }
+ printf ("Got lock: sleep_time = %d seconds\n", sleep_time);
+ sleep (sleep_time);
+ printf ("Closing '%s'\n", file_name);
+ close (fd);
+
+ return ret;
+}
diff --git a/libfshelp-tests/test-flock.c b/libfshelp-tests/test-flock.c
new file mode 100644
index 00000000..700aa25c
--- /dev/null
+++ b/libfshelp-tests/test-flock.c
@@ -0,0 +1,170 @@
+/* test-flock.c: Test advisory open file locks, see flock(2)
+ Options: <see below>
+
+ Copyright (C) 2016-2019 Free Software Foundation, Inc.
+
+ Written by Svante Signell <svante.signell@gmail.com>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <error.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/file.h>
+
+/* Parse args */
+int parse_args (int argc, char **argv, char **file_name,
+ int *flags, char **flagsc,
+ int *op, char **opc,
+ int *sleep_time)
+{
+ int i, tmp;
+ char *str, *endptr;
+
+ if (argc < 2)
+ error (1, 0, "Usage: %s file [flags] [operation] [sleep_time]\n\
+ file : file name/device name\n\
+ flags : r (O_RDONLY) | w (O_WRONLY) | rw (O_RDWR) : [w]\n\
+ operation : s (LOCK_SH), x (LOCK_EX), u (LOCK_UN),\n\
+ sn (LOCK_SH | LOCK_UN), xn (LOCK_EX | LOCK_UN) : [s]\n\
+ sleep_time : st <number> : [st 10]\n",
+ argv[0]);
+
+ *file_name = argv[1];
+ for (i = 2; i < argc; i++)
+ {
+ str = argv[i];
+ if (strncmp (str, "r", 2) == 0)
+ {
+ *flags = O_RDONLY;
+ *flagsc = "O_RDONLY";
+ continue;
+ }
+ if (strncmp (str, "w", 2) == 0)
+ {
+ *flags = O_WRONLY;
+ *flagsc = "O_WRONLY";
+ continue;
+ }
+ if (strncmp (str, "rw", 2) == 0)
+ {
+ *flags = O_RDWR;
+ *flagsc = "O_RDWR";
+ continue;
+ }
+ if (strncmp (str, "s", 2) == 0)
+ {
+ *op = LOCK_SH;
+ *opc = "LOCK_SH";
+ continue;
+ }
+ if (strncmp (str, "sn", 2) == 0)
+ {
+ *op = LOCK_SH | LOCK_NB;
+ *opc = "LOCK_SH | LOCK_NB";
+ continue;
+ }
+ if (strncmp (str, "x", 2) == 0)
+ {
+ *op = LOCK_EX;
+ *opc = "LOCK_EX";
+ continue;
+ }
+ if (strncmp (str, "xn", 2) == 0)
+ {
+ *op = LOCK_EX | LOCK_NB;
+ *opc = "LOCK_EX | LOCK_NB";
+ continue;
+ }
+ if (strncmp (str, "u", 2) == 0)
+ {
+ *op = LOCK_UN;
+ *opc = "LOCK_UN";
+ continue;
+ }
+ if (strncmp (str, "st", 2) == 0)
+ {
+ str = argv[++i];
+ if (str)
+ {
+ errno = 0;
+ tmp = strtol (str, &endptr, 10);
+ if (tmp == 0 && errno != 0)
+ error (1, errno, "%s", str);
+ if (endptr == str)
+ error (1, EINVAL, "%s", str);
+ *sleep_time = tmp;
+ }
+ else
+ error (1, EINVAL, "%s", str);
+ continue;
+ }
+ error (1, EINVAL, "%s", str);
+ }
+
+ return 0;
+}
+
+int main (int argc, char **argv)
+{
+#ifdef __GNU__
+ error_t err;
+#else
+ int err;
+#endif
+ int fd, ret = -1;
+ char *file_name = NULL;
+ int flags = O_RDONLY;
+ char *flagsc = "O_RDONLY";
+ int op = LOCK_SH;
+ char *opc = "LOCK_SH";
+ int sleep_time = 10;
+
+ ret = parse_args (argc, argv, &file_name,
+ &flags, &flagsc,
+ &op, &opc,
+ &sleep_time);
+
+#ifdef __GNU__
+ printf ("test-flock: GNU/Hurd\n");
+#else
+ printf ("test-flock: GNU/Linux\n");
+#endif
+
+ printf ("file = '%s', flags = %s\n", file_name, flagsc);
+ fd = open (file_name, flags);
+ if (fd < 0)
+ error (1, errno, "open");
+ printf ("Opening '%s', fd = %d, ", file_name, fd);
+ printf ("operation = %s\n", opc);
+ printf ("Requesting lock\n");
+ err = flock (fd, op);
+ if (err)
+ error (1, errno, "flock");
+
+ printf ("Got lock: sleep_time = %d seconds\n", sleep_time);
+ sleep (sleep_time);
+
+ printf ("Closing '%s'\n", file_name);
+ close (fd);
+
+ return ret;
+}
diff --git a/libfshelp-tests/test-lockf.c b/libfshelp-tests/test-lockf.c
new file mode 100644
index 00000000..1595cf35
--- /dev/null
+++ b/libfshelp-tests/test-lockf.c
@@ -0,0 +1,184 @@
+/* test-lockf.c: Test advisory open file record locks:
+ lockf(3) is an interface on top of fcntl(2) locking.
+ Options: <see below>
+
+ Copyright (C) 2016-2019 Free Software Foundation, Inc.
+
+ Written by Svante Signell <svante.signell@gmail.com>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <error.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/file.h>
+
+/* Parse args */
+int parse_args (int argc, char **argv, char **file_name,
+ int *flags, char **flagsc,
+ int *cmd, char **cmdc,
+ off_t *len, int *sleep_time)
+{
+ int i, tmp;
+ char *str, *endptr;
+
+ if (argc < 2)
+ error (1, 0, "Usage: %s file [flags] [cmd] [len] [sleep_time]\n\
+ file : file name/device name\n\
+ flags : r (O_RDONLY) | w (O_WRONLY) | rw (O_RDWR) : [w]\n\
+ cmd : x (F_LOCK), xt (F_TLOCK), u (F_ULOCK),\n\
+ t (F_TEST) : [x]\n\
+ len : l <number> : [0]\n\
+ sleep_time : st <number> : [st 10]\n",
+ argv[0]);
+
+ *file_name = argv[1];
+ for (i = 2; i < argc; i++)
+ {
+ str = argv[i];
+ if (strncmp (str, "r", 2) == 0)
+ {
+ *flags = O_RDONLY;
+ *flagsc = "O_RDONLY";
+ continue;
+ }
+ if (strncmp (str, "w", 2) == 0)
+ {
+ *flags = O_WRONLY;
+ *flagsc = "O_WRONLY";
+ continue;
+ }
+ if (strncmp (str, "rw", 2) == 0)
+ {
+ *flags = O_RDWR;
+ *flagsc = "O_RDWR";
+ continue;
+ }
+ if (strncmp (str, "x", 2) == 0)
+ {
+ *cmd = F_LOCK;
+ *cmdc = "F_LOCK";
+ continue;
+ }
+ if (strncmp (str, "xt", 2) == 0)
+ {
+ *cmd = F_TLOCK;
+ *cmdc = "F_TLOCK";
+ continue;
+ }
+ if (strncmp (str, "u", 2) == 0)
+ {
+ *cmd = F_ULOCK;
+ *cmdc = "F_LOCK";
+ continue;
+ }
+ if (strncmp (str, "t", 2) == 0)
+ {
+ *cmd = F_TEST;
+ *cmdc = "F_TEST";
+ continue;
+ }
+ if (strncmp (str, "l", 2) == 0)
+ {
+ str = argv[++i];
+ if (str)
+ {
+ errno = 0;
+ tmp = strtol (str, &endptr, 10);
+ if (tmp == 0 && errno != 0)
+ error (1, errno, "%s", str);
+ if (endptr == str)
+ error (1, EINVAL, "%s", str);
+ *len = tmp;
+ }
+ else
+ error (1, EINVAL, "%s", str);
+ continue;
+ }
+ if (strncmp (str, "st", 2) == 0)
+ {
+ str = argv[++i];
+ if (str)
+ {
+ errno = 0;
+ tmp = strtol (str, &endptr, 10);
+ if (tmp == 0 && errno != 0)
+ error (1, errno, "%s", str);
+ if (endptr == str)
+ error (1, EINVAL, "%s", str);
+ *sleep_time = tmp;
+ }
+ else
+ error (1, EINVAL, "%s", str);
+ continue;
+ }
+ error (1, EINVAL, "%s", str);
+ }
+
+ return 0;
+}
+
+int main (int argc, char **argv)
+{
+#ifdef __GNU__
+ error_t err;
+#else
+ int err;
+#endif
+ int fd, ret = -1;
+ char *file_name = NULL;
+ int flags = O_WRONLY;
+ char *flagsc = "O_WRONLY";
+ int cmd = F_LOCK;
+ char *cmdc = "F_LOCK";
+ off_t len = 0;
+ int sleep_time = 10;
+
+ ret = parse_args (argc, argv, &file_name,
+ &flags, &flagsc,
+ &cmd, &cmdc,
+ &len, &sleep_time);
+
+#ifdef __GNU__
+ printf ("test-lockf: GNU/Hurd\n");
+#else
+ printf ("test-lockf: GNU/Linux\n");
+#endif
+
+ printf ("file = '%s', flags = %s\n", file_name, flagsc);
+ fd = open (file_name, flags);
+ if (fd < 0)
+ error (1, errno, "open");
+ printf ("Opening '%s', fd = %d, ", file_name, fd);
+ printf ("cmd = %s, len = %ld\n", cmdc, len);
+ printf ("Requesting lock\n");
+ err = lockf (fd, cmd, len);
+ if (err)
+ error (1, errno, "lockf");
+
+ printf ("Got lock: sleep_time = %d seconds\n", sleep_time);
+ sleep (sleep_time);
+
+ printf ("Closing '%s'\n", file_name);
+ close (fd);
+
+ return ret;
+}