On Wed, 2023-10-04 at 22:30 +0500, Stas Sergeev wrote: > F_UNLCK can be used with F_OFD_GETLK to get the locks from > particular fd. This patch adds a new test, generic/732, > to check that functionality. > > The following options are added to t_ofd_locks: > -u selects F_UNLCK for testing > -G sets the unix socket name for fd receive > -S sets the unix socket name for fd send > > Test works as follows: > - locker process sets the lock and sends an fd via unix socket > - lock-tester process receives an fd and uses F_UNLCK to check the lock > > CC: fstests@xxxxxxxxxxxxxxx > CC: Murphy Zhou <xzhou@xxxxxxxxxx> > CC: Jeff Layton <jlayton@xxxxxxxxxx> > CC: Zorro Lang <zlang@xxxxxxxxxx> > > Signed-off-by: Stas Sergeev <stsp2@xxxxxxxxx> > --- > common/ofdlk | 61 +++++++++++++++ > common/rc | 4 +- > src/t_ofd_locks.c | 169 +++++++++++++++++++++++++++++++++++++++--- > tests/generic/478 | 59 +-------------- > tests/generic/732 | 60 +++++++++++++++ > tests/generic/732.out | 16 ++++ > 6 files changed, 298 insertions(+), 71 deletions(-) > create mode 100644 common/ofdlk > create mode 100755 tests/generic/732 > create mode 100644 tests/generic/732.out > > diff --git a/common/ofdlk b/common/ofdlk > new file mode 100644 > index 00000000..0cdd2250 > --- /dev/null > +++ b/common/ofdlk > @@ -0,0 +1,61 @@ > +# > +# Common functions for OFD lock tests > +# > + > +mk_sem() These are too generically named for functions that sit in common. Can you rename these to something more distinct? > +{ > + SEMID=$(ipcmk -S 2 | cut -d ":" -f 2 | tr -d '[:space:]') > + if [ -z "$SEMID" ]; then > + echo "ipcmk failed" > + exit 1 > + fi > + SEMKEY=$(ipcs -s | grep $SEMID | cut -d " " -f 1) > + if [ -z "$SEMKEY" ]; then > + echo "ipcs failed" > + exit 1 > + fi > +} > + > +rm_sem() > +{ > + ipcrm -s $SEMID 2>/dev/null > +} > + > +do_test() > +{ > + local soptions > + local goptions > + # print options and getlk output for debug > + echo $* >> $seqres.full 2>&1 > + mk_sem > + soptions="$1 -K $SEMKEY" > + goptions="$2 -K $SEMKEY" > + # -s : do setlk > + $here/src/t_ofd_locks $soptions $TEST_DIR/testfile & > + # -g : do getlk > + $here/src/t_ofd_locks $goptions $TEST_DIR/testfile | \ > + tee -a $seqres.full > + wait $! > + rm_sem > + > + mk_sem > + # add -F to clone with CLONE_FILES > + soptions="$1 -F -K $SEMKEY" > + goptions="$2 -K $SEMKEY" > + # with -F, new locks are always file to place > + $here/src/t_ofd_locks $soptions $TEST_DIR/testfile & > + $here/src/t_ofd_locks $goptions $TEST_DIR/testfile | \ > + tee -a $seqres.full > + wait $! > + rm_sem > + > + mk_sem > + # add -d to dup and close > + soptions="$1 -d -K $SEMKEY" > + goptions="$2 -K $SEMKEY" > + $here/src/t_ofd_locks $soptions $TEST_DIR/testfile & > + $here/src/t_ofd_locks $goptions $TEST_DIR/testfile | \ > + tee -a $seqres.full > + wait $! > + rm_sem > +} > diff --git a/common/rc b/common/rc > index 1618ded5..1111fc12 100644 > --- a/common/rc > +++ b/common/rc > @@ -4222,8 +4222,8 @@ _require_ofd_locks() > # EINVAL will be returned. > _require_test_program "t_ofd_locks" > touch $TEST_DIR/ofd_testfile > - $here/src/t_ofd_locks -t $TEST_DIR/ofd_testfile > /dev/null 2>&1 > - [ $? -eq 22 ] && _notrun "Require OFD locks support" > + $here/src/t_ofd_locks -t $1 $TEST_DIR/ofd_testfile > /dev/null 2>&1 > + [ $? -eq 22 ] && _notrun "Require OFD locks support $2" > } > > Instead of adding extra parameters to this function, it would be better to just add a new _require_ofd_getlk_unlck() function that does what's needed for this test. There may be other testcases that require that functionality and its more self-documenting for later users. > > _require_test_lsattr() > diff --git a/src/t_ofd_locks.c b/src/t_ofd_locks.c > index 58cb0959..8d63b80b 100644 > --- a/src/t_ofd_locks.c > +++ b/src/t_ofd_locks.c > @@ -14,6 +14,8 @@ > #include <sys/wait.h> > #include <sys/ipc.h> > #include <sys/sem.h> > +#include <sys/socket.h> > +#include <sys/un.h> > > /* > * In distributions that do not have these macros ready in glibc-headers, > @@ -134,10 +136,13 @@ static void usage(char *arg0) > printf("\t-F : clone with CLONE_FILES in setlk to setup test condition\n"); > printf("\t-d : dup and close in setlk\n"); > printf("\twithout both -F/d, use clone without CLONE_FILES\n"); > - printf("\t-r/-w : set/get rdlck/wrlck\n"); > + printf("\t-r/-w/-u : set/get rdlck/wrlck/unlck\n"); > printf("\t-o num : offset start to lock, default 0\n"); > printf("\t-l num : lock length, default 10\n"); > printf("\t-R/-W : open file RDONLY/RDWR\n\n"); > + printf("\t-G path : unix socket path for fd reception\n\n"); > + printf("\t-S path : unix socket path for fd sending\n\n"); > + printf("\t-t : test run, try to perform lock and report\n\n"); > printf("\tUsually we run a setlk routine in background and then\n"); > printf("\trun a getlk routine to check. They must be paired, or\n"); > printf("\ttest will hang.\n\n"); > @@ -164,6 +169,117 @@ static int child_fn(void* p) > return 0; > } > > +static int connect_unix(const char *sname) > +{ > + struct sockaddr_un addr = { .sun_family = AF_UNIX }; > + > + int unix_sock = socket(AF_UNIX, SOCK_DGRAM, 0); > + if (unix_sock == -1) > + return -1; > + > + strncpy(addr.sun_path, sname, sizeof(addr.sun_path) - 1); > + if (connect(unix_sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) > + { > + close(unix_sock); > + perror("connect()"); > + return -1; > + } > + > + return unix_sock; > +} > + > +static int send_fd(const char *sname, int fd) > +{ > + int ret; > + int unix_sock; > + struct iovec iov = {.iov_base = ":)", // Must send at least one byte > + .iov_len = 2}; > + > + union { > + char buf[CMSG_SPACE(sizeof(fd))]; > + struct cmsghdr align; > + } u; > + > + struct msghdr msg = {.msg_iov = &iov, > + .msg_iovlen = 1, > + .msg_control = u.buf, > + .msg_controllen = sizeof(u.buf)}; > + > + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); > + *cmsg = (struct cmsghdr){.cmsg_level = SOL_SOCKET, > + .cmsg_type = SCM_RIGHTS, > + .cmsg_len = CMSG_LEN(sizeof(fd))}; > + > + memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); > + > + unix_sock = connect_unix(sname); > + if (unix_sock == -1) { > + perror("sendmsg()"); > + return -1; > + } > + ret = sendmsg(unix_sock, &msg, 0); > + close(unix_sock); > + if (ret == -1) > + perror("sendmsg()"); > + return ret; > +} > + > +static int bind_unix(const char *sname) > +{ > + struct sockaddr_storage storage = {}; > + struct sockaddr_un *addr; > + int sock; > + > + sock = socket(AF_UNIX, SOCK_DGRAM, 0); > + if (sock == -1) > + return -1; > + addr = (struct sockaddr_un *)&storage; > + addr->sun_family = AF_UNIX; > + strncpy(addr->sun_path, sname, sizeof(addr->sun_path) - 1); > + unlink(sname); > + if (bind(sock, (struct sockaddr *)addr, SUN_LEN(addr)) == -1) { > + perror("bind()"); > + close(sock); > + return -1; > + } > + return sock; > +} > + > +static int recv_fd(int sock) > +{ > + struct msghdr msg; > + struct cmsghdr *cmsghdr; > + struct iovec iov; > + ssize_t nbytes; > + int *p; > + char buf[CMSG_SPACE(sizeof(int))], c[16]; > + struct cmsghdr *cmsgp; > + > + iov.iov_base = &c; > + iov.iov_len = sizeof(c); > + cmsghdr = (struct cmsghdr *)buf; > + cmsghdr->cmsg_len = CMSG_LEN(sizeof(int)); > + cmsghdr->cmsg_level = SOL_SOCKET; > + cmsghdr->cmsg_type = SCM_RIGHTS; > + msg.msg_name = NULL; > + msg.msg_namelen = 0; > + msg.msg_iov = &iov; > + msg.msg_iovlen = 1; > + msg.msg_control = cmsghdr; > + msg.msg_controllen = CMSG_LEN(sizeof(int)); > + msg.msg_flags = 0; > + > + nbytes = recvmsg(sock, &msg, 0); > + if (nbytes == -1) > + return -1; > + > + cmsgp = CMSG_FIRSTHDR(&msg); > + p = (int *)CMSG_DATA(cmsgp); > + if (!p) > + return -1; > + return *p; > +} > + > int main(int argc, char **argv) > { > int posix = 0; > @@ -184,10 +300,12 @@ int main(int argc, char **argv) > struct semid_ds sem_ds; > struct sembuf sop; > int opt, ret; > + int rcv_sock = -1; > + const char *snd_sock_name = NULL; > > //avoid libcap errno bug > errno = 0; > - while((opt = getopt(argc, argv, "sgrwo:l:PRWtFdK:")) != -1) { > + while((opt = getopt(argc, argv, "sgrwuo:l:PRWtFdK:G:S:")) != -1) { > switch(opt) { > case 's': > lock_cmd = 1; > @@ -201,6 +319,9 @@ int main(int argc, char **argv) > case 'w': > lock_rw = 1; > break; > + case 'u': > + lock_rw = 2; > + break; > case 'o': > lock_start = atoi(optarg); > break; > @@ -228,6 +349,12 @@ int main(int argc, char **argv) > case 'K': > semkey = strtol(optarg, NULL, 16); > break; > + case 'G': > + rcv_sock = bind_unix(optarg); > + break; > + case 'S': > + snd_sock_name = optarg; > + break; > default: > usage(argv[0]); > return -1; > @@ -256,17 +383,27 @@ int main(int argc, char **argv) > getlk_macro = F_GETLK; > } > > - if (lock_rw == 1) > - flk.l_type = F_WRLCK; > - else > + switch (lock_rw) { > + case 0: > flk.l_type = F_RDLCK; > + break; > + case 1: > + flk.l_type = F_WRLCK; > + break; > + case 2: > + flk.l_type = F_UNLCK; > + break; > + } > > - if (open_rw == 0) > - fd = open(argv[optind], O_RDONLY); > - else > - fd = open(argv[optind], O_RDWR); > - if (fd == -1) > - err_exit("open", errno); > + if (rcv_sock == -1) { > + if (open_rw == 0) { > + fd = open(argv[optind], O_RDONLY); > + } else { > + fd = open(argv[optind], O_RDWR); > + } > + if (fd == -1) > + err_exit("open", errno); > + } > > /* > * In a testun, we do a fcntl getlk call and exit > @@ -322,6 +459,9 @@ int main(int argc, char **argv) > if (fcntl(fd, setlk_macro, &flk) < 0) > err_exit("setlkw", errno); > > + if (snd_sock_name) > + send_fd(snd_sock_name, fd); > + > if (use_dup == 1) { > /* dup fd and close the newfd */ > int dfd = dup(fd); > @@ -400,6 +540,13 @@ int main(int argc, char **argv) > if (semtimedop(semid, &sop, 1, &ts) == -1) > err_exit("wait sem0 0", errno); > > + if (rcv_sock != -1) { > + fd = recv_fd(rcv_sock); > + close(rcv_sock); > + if (fd == -1) > + err_exit("SCM_RIGHTS", errno); > + } > + > if (fcntl(fd, getlk_macro, &flk) < 0) > err_exit("getlk", errno); > > diff --git a/tests/generic/478 b/tests/generic/478 > index 419acc94..580c260e 100755 > --- a/tests/generic/478 > +++ b/tests/generic/478 > @@ -88,6 +88,7 @@ _begin_fstest auto quick > > # Import common functions. > . ./common/filter > +. ./common/ofdlk > > # Modify as appropriate. > _supported_fs generic > @@ -99,64 +100,6 @@ _require_ofd_locks > $XFS_IO_PROG -f -c "pwrite -S 0xFF 0 4096" \ > $TEST_DIR/testfile >> $seqres.full 2>&1 > > -mk_sem() > -{ > - SEMID=$(ipcmk -S 2 | cut -d ":" -f 2 | tr -d '[:space:]') > - if [ -z "$SEMID" ]; then > - echo "ipcmk failed" > - exit 1 > - fi > - SEMKEY=$(ipcs -s | grep $SEMID | cut -d " " -f 1) > - if [ -z "$SEMKEY" ]; then > - echo "ipcs failed" > - exit 1 > - fi > -} > - > -rm_sem() > -{ > - ipcrm -s $SEMID 2>/dev/null > -} > - > -do_test() > -{ > - local soptions > - local goptions > - # print options and getlk output for debug > - echo $* >> $seqres.full 2>&1 > - mk_sem > - soptions="$1 -K $SEMKEY" > - goptions="$2 -K $SEMKEY" > - # -s : do setlk > - $here/src/t_ofd_locks $soptions $TEST_DIR/testfile & > - # -g : do getlk > - $here/src/t_ofd_locks $goptions $TEST_DIR/testfile | \ > - tee -a $seqres.full > - wait $! > - rm_sem > - > - mk_sem > - # add -F to clone with CLONE_FILES > - soptions="$1 -F -K $SEMKEY" > - goptions="$2 -K $SEMKEY" > - # with -F, new locks are always file to place > - $here/src/t_ofd_locks $soptions $TEST_DIR/testfile & > - $here/src/t_ofd_locks $goptions $TEST_DIR/testfile | \ > - tee -a $seqres.full > - wait $! > - rm_sem > - > - mk_sem > - # add -d to dup and close > - soptions="$1 -d -K $SEMKEY" > - goptions="$2 -K $SEMKEY" > - $here/src/t_ofd_locks $soptions $TEST_DIR/testfile & > - $here/src/t_ofd_locks $goptions $TEST_DIR/testfile | \ > - tee -a $seqres.full > - wait $! > - rm_sem > -} > - > # Always setlk at range [0,9], getlk at range [0,9] [5,24] or [20,29]. > # To open file RDONLY or RDWR should not break the locks. > # POSIX locks should be released after closed fd, so it wont conflict > diff --git a/tests/generic/732 b/tests/generic/732 > new file mode 100755 > index 00000000..78f5bd44 > --- /dev/null > +++ b/tests/generic/732 > @@ -0,0 +1,60 @@ > +#! /bin/bash > +# SPDX-License-Identifier: GPL-2.0 > +# Copyright (c) 2023 stsp2@xxxxxxxxx > +# > +# FS QA Test 732 > +# > +# Test OFD lock's F_UNLCK extension. > +# > +. ./common/preamble > +_begin_fstest auto quick > + > +# Import common functions. > +. ./common/filter > +. ./common/ofdlk > + > +# Modify as appropriate. > +_supported_fs generic > +_require_test > +_require_ofd_locks -gu "F_OFD_GETLK/F_UNLCK extension" > + > +# real QA test starts here > +# prepare a 4k testfile in TEST_DIR > +$XFS_IO_PROG -f -c "pwrite -S 0xFF 0 4096" \ > + $TEST_DIR/testfile >> $seqres.full 2>&1 > + > +# Always setlk at range [0,9], getlk at range [0,9] [5,24] or [20,29]. > +# To open file RDONLY or RDWR should not break the locks. > +# POSIX locks should be released after closed fd, so it wont conflict > +# with other locks in tests > + > +# -P : operate posix lock > +# -w : operate on F_WRLCK > +# -r : operate on F_RDLCK > +# -R : open file RDONLY > +# -W : open file RDWR > +# -o : file offset where the lock starts > +# -l : lock length > +# -F : clone with CLONE_FILES in setlk > +# -d : dup and close in setlk > + > +# setlk wrlck [0,9], getlk wrlck [0,9], expect > +# + wrlck when CLONE_FILES not set > +# + unlck when CLONE_FILES set > +# + wrlck when dup & close > + > +# setlk wrlck [0,9], getlk unlck [0,9] > +do_test "-s -w -o 0 -l 10 -W -S /tmp/fstests.sock" "-g -u -o 0 -l 10 -W -G /tmp/fstests.sock" "wrlck" "wrlck" "wrlck" > +# setlk wrlck [0,9], getlk unlck [20,29] > +do_test "-s -w -o 0 -l 10 -S /tmp/fstests.sock" "-g -u -o 20 -l 10 -G /tmp/fstests.sock" "unlck" "unlck" "unlck" > + > +# setlk rdlck [0,9], getlk unlck [0,9], open RDONLY > +do_test "-s -r -o 0 -l 10 -R -S /tmp/fstests.sock" "-g -u -o 0 -l 10 -R -G /tmp/fstests.sock" "rdlck" "rdlck" "rdlck" > +# setlk posix rdlck [0,9], getlk unlck [5,24], open RDONLY > +do_test "-s -r -o 0 -l 10 -R -S /tmp/fstests.sock" "-g -u -o 5 -l 20 -R -G /tmp/fstests.sock" "rdlck" "rdlck" "rdlck" > +# setlk rdlck [0,9], getlk unlck [20,29], open RDONLY > +do_test "-s -r -o 0 -l 10 -R -S /tmp/fstests.sock" "-g -u -o 20 -l 10 -R -G /tmp/fstests.sock" "unlck" "unlck" "unlck" > + > +# success, all done > +status=0 > +exit > diff --git a/tests/generic/732.out b/tests/generic/732.out > new file mode 100644 > index 00000000..98996904 > --- /dev/null > +++ b/tests/generic/732.out > @@ -0,0 +1,16 @@ > +QA output created by 732 > +get wrlck > +get wrlck > +get wrlck > +lock could be placed > +lock could be placed > +lock could be placed > +get rdlck > +get rdlck > +get rdlck > +get rdlck > +get rdlck > +get rdlck > +lock could be placed > +lock could be placed > +lock could be placed I think the rest looks good though. Thanks for doing this. Cheers, -- Jeff Layton <jlayton@xxxxxxxxxx>