| /* |
| * |
| * Copyright (c) International Business Machines Corp., 2001 |
| * |
| * 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 of the License, 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 this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| /* |
| * NAME |
| * fcntl14.c |
| * |
| * DESCRIPTION |
| * File locking test cases for fcntl. In Linux, S_ENFMT is not implemented |
| * in the kernel. However all standard Unix kernels define S_ENFMT as |
| * S_ISGID. So this test defines S_ENFMT as S_ISGID. |
| * |
| * ALGORITHM |
| * Various test cases are used to lock a file opened without mandatory |
| * locking, with mandatory locking and mandatory locking with NOBLOCK |
| * |
| * USAGE |
| * fcntl14 |
| * |
| * HISTORY |
| * 07/2001 Ported by Wayne Boyer |
| * |
| * RESTRICTIONS |
| * None |
| */ |
| #define _GNU_SOURCE 1 |
| #include <fcntl.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <signal.h> |
| #include <errno.h> |
| #include <sys/wait.h> |
| #include <inttypes.h> |
| #include "usctest.h" |
| #include "test.h" |
| #include "tst_fs_type.h" |
| |
| #define SKIP 0x0c00 |
| #if SKIP == F_RDLCK || SKIP== F_WRLCK |
| #error invalid value for SKIP, must be distinct from F_RDLCK and F_WRLCK |
| #endif |
| #ifndef S_ENFMT |
| #define S_ENFMT S_ISGID |
| #endif |
| |
| /* NOBLOCK - immediate success */ |
| #define NOBLOCK 2 |
| |
| /* WILLBLOCK - blocks, then succeeds (parent must unlock records) */ |
| #define WILLBLOCK 3 |
| |
| #define TIME_OUT 60 |
| |
| typedef struct { |
| short a_type; |
| short a_whence; |
| long a_start; |
| long a_len; |
| short b_type; /* SKIP means suppress fcntl call */ |
| short b_whence; |
| long b_start; |
| long b_len; |
| short c_type; |
| int c_whence; |
| long c_start; |
| long c_len; |
| short c_flag; |
| } testcase; |
| |
| static testcase testcases[] = { |
| /* Test cases: entire boundary */ |
| /* #1 Parent making a write lock on entire file */ |
| {F_WRLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L, |
| /* Child attempting a read lock on entire file */ |
| F_RDLCK, 0, 0L, 0L, WILLBLOCK}, |
| |
| /* #2 Parent making a write lock on entire file */ |
| {F_WRLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L, |
| /* Child attempting a write lock on entire file */ |
| F_WRLCK, 0, 0L, 0L, WILLBLOCK}, |
| |
| /* #3 Parent making a read lock on entire file */ |
| {F_RDLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L, |
| /* Child attempting a read lock on entire file */ |
| F_RDLCK, 0, 0L, 0L, NOBLOCK}, |
| |
| /* #4 Parent making a read lock on entire file */ |
| {F_RDLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L, |
| /* Child attempting a write lock on entire file */ |
| F_WRLCK, 0, 0L, 0L, WILLBLOCK}, |
| |
| /* Test case: start boundary */ |
| /* #5 Parent making a write lock on entire file */ |
| {F_WRLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L, |
| /* |
| * Child attempting a read lock from beginning of |
| * file for 5 bytes |
| */ |
| F_RDLCK, 0, 0L, 5L, WILLBLOCK}, |
| |
| /* #6 Parent making a write lock on entire file */ |
| {F_WRLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L, |
| /* |
| * Child attempting a write lock from beginning of |
| * file for 5 bytes |
| */ |
| F_WRLCK, 0, 0L, 5L, WILLBLOCK}, |
| |
| /* #7 Parent making a read lock on entire file */ |
| {F_RDLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L, |
| /* |
| * Child attempting a read lock from beginning of |
| * file for 5 bytes |
| */ |
| F_RDLCK, 0, 0L, 5L, NOBLOCK}, |
| |
| /* #8 Parent making a read lock on entire file */ |
| {F_RDLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L, |
| /* |
| * Child attempting a write lock from beginning of |
| * file for 5 bytes |
| */ |
| F_WRLCK, 0, 0L, 5L, WILLBLOCK}, |
| |
| /* Test cases: end boundary */ |
| /* #9 Parent making a write lock on entire file */ |
| {F_WRLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L, |
| /* Child attempting a read lock from byte 7 to end of file */ |
| F_RDLCK, 0, 7L, 0L, WILLBLOCK}, |
| |
| /* #10 Parent making a write lock on entire file */ |
| {F_WRLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L, |
| /* Child attempting a write lock from byte 7 to end of file */ |
| F_WRLCK, 0, 7L, 0L, WILLBLOCK}, |
| |
| /* #11 Parent making a read lock on entire file */ |
| {F_RDLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L, |
| /* Child attempting a read lock from byte 7 to end of file */ |
| F_RDLCK, 0, 7L, 0L, NOBLOCK}, |
| |
| /* #12 Parent making a read lock on entire file */ |
| {F_RDLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L, |
| /* Child attempting a write lock from byte 7 to end of file */ |
| F_WRLCK, 0, 7L, 0L, WILLBLOCK}, |
| |
| /* Test cases: entire boundary ( less than entire file) */ |
| /* |
| * #13 Parent making a write lock from beginning of |
| * file for 5 bytes |
| */ |
| {F_WRLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, |
| /* |
| * Child attempting a read lock from beginning of |
| * file for 5 bytes |
| */ |
| F_RDLCK, 0, 0L, 5L, WILLBLOCK}, |
| |
| /* |
| * #14 Parent making a write lock from beginning of file |
| * for 5 bytes |
| */ |
| {F_WRLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, |
| /* |
| * Child attempting a write lock from beginning of |
| * file for 5 bytes |
| */ |
| F_WRLCK, 0, 0L, 5L, WILLBLOCK}, |
| |
| /* |
| * #15 Parent making a read lock from beginning of |
| * file for 5 bytes |
| */ |
| {F_RDLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, |
| /* |
| * Child attempting a read lock from beginning of |
| * file for 5 bytes |
| */ |
| F_RDLCK, 0, 0L, 5L, NOBLOCK}, |
| |
| /* |
| * #16 Parent making a read lock from beginning of |
| * file for 5 bytes |
| */ |
| {F_RDLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, |
| /* |
| * Child attempting a write lock from beginning |
| * of file for 5 bytes |
| */ |
| F_WRLCK, 0, 0L, 5L, WILLBLOCK}, |
| |
| /* Test cases: inside boundary */ |
| /* |
| * #17 Parent making a write lock from beginning |
| * of file for 5 bytes |
| */ |
| {F_WRLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, |
| /* Child attempting a read lock from byte 2 to byte 4 */ |
| F_RDLCK, 0, 1L, 3L, WILLBLOCK}, |
| |
| /* |
| * #18 Parent making a write lock from beginning of |
| * file for 5 bytes |
| */ |
| {F_WRLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, |
| /* Child attempting a write lock from byte 2 to byte 4 */ |
| F_WRLCK, 0, 1L, 3L, WILLBLOCK}, |
| |
| /* |
| * #19 Parent making a read lock from beginning of |
| * file for 5 bytes |
| */ |
| {F_RDLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, |
| /* Child attempting a read lock from byte 2 to byte 4 */ |
| F_RDLCK, 0, 1L, 3L, NOBLOCK}, |
| |
| /* |
| * #20 Parent making a read lock from beginning of |
| * file for 5 bytes |
| */ |
| {F_RDLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, |
| /* Child attempting a write lock from byte 2 to byte 4 */ |
| F_WRLCK, 0, 1L, 3L, WILLBLOCK}, |
| |
| /* Test cases: cross boundary (inside to after) */ |
| /* |
| * #21 Parent making a write lock from beginning of |
| * file for 5 bytes |
| */ |
| {F_WRLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, |
| /* Child attempting a read lock from byte 3 to byte 7 */ |
| F_RDLCK, 0, 2L, 5L, WILLBLOCK}, |
| |
| /* |
| * #22 Parent making a write lock from beginning |
| * of file for 5 bytes |
| */ |
| {F_WRLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, |
| /* Child attempting a write lock from byte 3 to byte 7 */ |
| F_WRLCK, 0, 2L, 5L, WILLBLOCK}, |
| |
| /* |
| * #23 Parent making a read lock from beginning of |
| * file for 5 bytes |
| */ |
| {F_RDLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, |
| /* Child attempting a read lock from byte 3 to byte 7 */ |
| F_RDLCK, 0, 2L, 5L, NOBLOCK}, |
| |
| /* |
| * #24 Parent making a read lock from beginning of |
| * file for 5 bytes |
| */ |
| {F_RDLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, |
| /* Child attempting a write lock from byte 3 to byte 7 */ |
| F_WRLCK, 0, 2L, 5L, WILLBLOCK}, |
| |
| /* Test cases: outside boundary (after) */ |
| |
| /* |
| * #25 Parent making a write lock from beginning of |
| * file for 5 bytes |
| */ |
| {F_WRLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, |
| /* Child attempting a read lock from byte 7 to end of file */ |
| F_RDLCK, 0, 6L, 0L, NOBLOCK}, |
| |
| /* |
| * #26 Parent making a write lock from beginning of |
| * file for 5 bytes |
| */ |
| {F_WRLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, |
| /* Child attempting a write lock from byte 7 to end of file */ |
| F_WRLCK, 0, 6L, 0L, NOBLOCK}, |
| |
| /* |
| * #27 Parent making a read lock from beginning of |
| * file for 5 bytes |
| */ |
| {F_RDLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, |
| /* Child attempting a read lock from byte 7 to end of file */ |
| F_RDLCK, 0, 6L, 0L, NOBLOCK}, |
| |
| /* |
| * #28 Parent making a read lock from beginning of |
| * file for 5 bytes |
| */ |
| {F_RDLCK, 0, 0L, 5L, SKIP, 0, 0L, 0L, |
| /* Child attempting a write lock from byte 7 to end of file */ |
| F_WRLCK, 0, 6L, 0L, NOBLOCK}, |
| |
| /* Test cases: outside boundary (before) */ |
| |
| /* #29 Parent making a write lock from byte 3 to byte 7 */ |
| {F_WRLCK, 0, 2L, 5L, SKIP, 0, 0L, 0L, |
| /* Child attempting a read lock from beginning of file to byte 2 */ |
| F_RDLCK, 0, 0L, 2L, NOBLOCK}, |
| |
| /* #30 Parent making a write lock from byte 3 to byte 7 */ |
| {F_WRLCK, 0, 2L, 5L, SKIP, 0, 0L, 0L, |
| /* Child attempting a write lock from beginning of file to byte 2 */ |
| F_WRLCK, 0, 0L, 2L, NOBLOCK}, |
| |
| /* #31 Parent making a write lock from byte 3 to byte 7 */ |
| {F_RDLCK, 0, 2L, 5L, SKIP, 0, 0L, 0L, |
| /* Child attempting a read lock from beginning of file to byte 2 */ |
| F_RDLCK, 0, 0L, 2L, NOBLOCK}, |
| |
| /* #32 Parent making a write lock from byte 3 to byte 7 */ |
| {F_RDLCK, 0, 2L, 5L, SKIP, 0, 0L, 0L, |
| /* Child attempting a write lock from beginning of file to byte 2 */ |
| F_WRLCK, 0, 0L, 2L, NOBLOCK}, |
| |
| /* Test cases: cross boundary (before to inside) */ |
| /* #33 Parent making a write lock from byte 5 to end of file */ |
| {F_WRLCK, 0, 4L, 0L, SKIP, 0, 0L, 0L, |
| /* Child attempting a read lock from byte 3 to byte 7 */ |
| F_RDLCK, 0, 2L, 5L, WILLBLOCK}, |
| |
| /* #34 Parent making a write lock from byte 5 to end of file */ |
| {F_WRLCK, 0, 4L, 0L, SKIP, 0, 0L, 0L, |
| /* Child attempting a write lock from byte 3 to byte 7 */ |
| F_WRLCK, 0, 2L, 5L, WILLBLOCK}, |
| |
| /* #35 Parent making a read lock from byte 5 to end of file */ |
| {F_RDLCK, 0, 4L, 0L, SKIP, 0, 0L, 0L, |
| /* Child attempting a read lock from byte 3 to byte 7 */ |
| F_RDLCK, 0, 2L, 5L, NOBLOCK}, |
| |
| /* #36 Parent making a read lock from byte 5 to end of file */ |
| {F_RDLCK, 0, 4L, 0L, SKIP, 0, 0L, 0L, |
| /* Child attempting a write lock from byte 3 to byte 7 */ |
| F_WRLCK, 0, 2L, 5L, WILLBLOCK}, |
| |
| /* Start of negative L_start and L_len test cases */ |
| /* |
| * #37 Parent making write lock from byte 2 to byte 3 |
| * with L_start = -3 |
| */ |
| {F_WRLCK, 1, -3L, 2L, SKIP, 0, 0L, 0L, |
| /* Child attempting write lock on byte 1 */ |
| F_WRLCK, 0, 1L, 1L, NOBLOCK}, |
| |
| /* |
| * #38 Parent making write lock from byte 2 to byte 3 |
| * with L_start = -3 |
| */ |
| {F_WRLCK, 1, -3L, 2L, SKIP, 0, 0L, 0L, |
| /* Child attempting write lock on byte 4 */ |
| F_WRLCK, 0, 4L, 1L, NOBLOCK}, |
| |
| /* |
| * #39 Parent making write lock from byte 2 to byte 3 |
| * with L_start = -3 |
| */ |
| {F_WRLCK, 1, -3L, 2L, SKIP, 0, 0L, 0L, |
| /* Child attempting write lock on byte 2 */ |
| F_WRLCK, 0, 2L, 1L, WILLBLOCK}, |
| |
| /* |
| * #40 Parent making write lock from byte 2 to byte 3 |
| * with L_start = -3 |
| */ |
| {F_WRLCK, 1, -3L, 2L, SKIP, 0, 0L, 0L, |
| /* Child attempting write lock on byte 3 */ |
| F_WRLCK, 0, 3L, 1L, WILLBLOCK}, |
| |
| /* |
| * #41 Parent making write lock from byte 2 to byte 6 |
| * with L_start = -3 |
| */ |
| {F_WRLCK, 1, -3L, 5L, SKIP, 0, 0L, 0L, |
| /* Child attempting write lock on byte 1 */ |
| F_WRLCK, 0, 1L, 1L, NOBLOCK}, |
| |
| /* |
| * #42 Parent making write lock from byte 2 to byte 6 |
| * with L_start = -3 |
| */ |
| {F_WRLCK, 1, -3L, 5L, SKIP, 0, 0L, 0L, |
| /* Child attempting write lock on byte 7 */ |
| F_WRLCK, 0, 1L, 1L, NOBLOCK}, |
| |
| /* |
| * #43 Parent making write lock from byte 2 to byte 6 |
| * with L_start = -3 |
| */ |
| {F_WRLCK, 1, -3L, 5L, SKIP, 0, 0L, 0L, |
| /* Child attempting write lock on byte 2 */ |
| F_WRLCK, 0, 2L, 1L, WILLBLOCK}, |
| |
| /* |
| * #44 Parent making write lock from byte 2 to byte 6 |
| * with L_start = -3 |
| */ |
| {F_WRLCK, 1, -3L, 5L, SKIP, 0, 0L, 0L, |
| /* Child attempting write lock on byte 5 */ |
| F_WRLCK, 0, 5L, 1L, WILLBLOCK}, |
| |
| /* |
| * #45 Parent making write lock from byte 2 to byte 6 |
| * with L_start = -3 |
| */ |
| {F_WRLCK, 1, -3L, 5L, SKIP, 0, 0L, 0L, |
| /* Child attempting write lock on byte 6 */ |
| F_WRLCK, 0, 6L, 1L, WILLBLOCK}, |
| |
| /* |
| * #46 Parent making write lock from byte 2 to byte 3 with |
| * L_start = -2 and L_len = -2 |
| */ |
| {F_WRLCK, 1, 2L, -2L, SKIP, 0, 0L, 0L, |
| /* Child attempting write lock on byte 1 */ |
| F_WRLCK, 0, 1L, 1L, NOBLOCK}, |
| |
| /* |
| * #47 Parent making write lock from byte 2 to byte 3 with |
| * L_start = -2 and L_len = -2 |
| */ |
| {F_WRLCK, 1, -2L, -2L, SKIP, 0, 0L, 0L, |
| /* Child attempting write lock on byte 4 */ |
| F_WRLCK, 0, 4L, 1L, NOBLOCK}, |
| |
| /* |
| * #48 Parent making write lock from byte 2 to byte 3 with |
| * L_start = -2 and L_len = -2 |
| */ |
| {F_WRLCK, 1, -2L, -2L, SKIP, 0, 0L, 0L, |
| /* Child attempting write lock on byte 2 */ |
| F_WRLCK, 0, 2L, 1L, WILLBLOCK}, |
| |
| /* |
| * #49 Parent making write lock from byte 2 to byte 3 with |
| * L_start = -2 and L_len = -2 |
| */ |
| {F_WRLCK, 1, -2L, -2L, SKIP, 0, 0L, 0L, |
| /* Child attempting write lock on byte 3 */ |
| F_WRLCK, 0, 3L, 1L, WILLBLOCK}, |
| |
| /* |
| * #50 Parent making write lock from byte 6 to byte 7 with |
| * L_start = 2 and L_len = -2 |
| */ |
| {F_WRLCK, 1, 2L, -2L, SKIP, 0, 0L, 0L, |
| /* Child attempting write lock on byte 5 */ |
| F_WRLCK, 0, 5L, 1L, NOBLOCK}, |
| |
| /* |
| * #51 Parent making write lock from byte 6 to byte 7 with |
| * L_start = 2 and L_len = -2 |
| */ |
| {F_WRLCK, 1, 2L, -2L, SKIP, 0, 0L, 0L, |
| /* Child attempting write lock on byte 8 */ |
| F_WRLCK, 0, 8L, 1L, NOBLOCK}, |
| |
| /* |
| * #52 Parent making write lock from byte 6 to byte 7 with |
| * L_start = 2 and L_len = -2 |
| */ |
| {F_WRLCK, 1, 2L, -2L, SKIP, 0, 0L, 0L, |
| /* Child attempting write lock on byte 6 */ |
| F_WRLCK, 0, 6L, 1L, WILLBLOCK}, |
| |
| /* |
| * #53 Parent making write lock from byte 6 to byte 7 with |
| * L_start = 2 and L_len = -2 |
| */ |
| {F_WRLCK, 1, 2L, -2L, SKIP, 0, 0L, 0L, |
| /* Child attempting write lock on byte 7 */ |
| F_WRLCK, 0, 7L, 1L, WILLBLOCK}, |
| |
| /* |
| * #54 Parent making write lock from byte 3 to byte 7 with |
| * L_start = 2 and L_len = -5 |
| */ |
| {F_WRLCK, 1, 2L, -5L, SKIP, 0, 0L, 0L, |
| /* Child attempting write lock on byte 2 */ |
| F_WRLCK, 0, 2L, 1L, NOBLOCK}, |
| |
| /* |
| * #55 Parent making write lock from byte 3 to byte 7 with |
| * L_start = 2 and L_len = -5 |
| */ |
| {F_WRLCK, 1, 2L, -5L, SKIP, 0, 0L, 0L, |
| /* Child attempting write lock on byte 8 */ |
| F_WRLCK, 0, 8L, 1L, NOBLOCK}, |
| |
| /* |
| * #56 Parent making write lock from byte 3 to byte 7 with |
| * L_start = 2 and L_len = -5 |
| */ |
| {F_WRLCK, 1, 2L, -5L, SKIP, 0, 0L, 0L, |
| /* Child attempting write lock on byte 3 */ |
| F_WRLCK, 0, 3L, 1L, WILLBLOCK}, |
| |
| /* |
| * #57 Parent making write lock from byte 3 to byte 7 with |
| * L_start = 2 and L_len = -5 |
| */ |
| {F_WRLCK, 1, 2L, -5L, SKIP, 0, 0L, 0L, |
| /* Child attempting write lock on byte 5 */ |
| F_WRLCK, 0, 5L, 1L, WILLBLOCK}, |
| |
| /* |
| * #58 Parent making write lock from byte 3 to byte 7 with |
| * L_start = 2 and L_len = -5 |
| */ |
| {F_WRLCK, 1, 2L, -5L, SKIP, 0, 0L, 0L, |
| /* Child attempting write lock on byte 7 */ |
| F_WRLCK, 0, 7L, 1L, WILLBLOCK}, |
| |
| /* Test case for block 4 */ |
| /* #59 Parent making write lock on entire file */ |
| {F_WRLCK, 0, 0L, 0L, SKIP, 0, 0L, 0L, |
| /* Child attempting write lock on byte 15 to end of file */ |
| F_WRLCK, 0, 15L, 0L, WILLBLOCK}, |
| }; |
| |
| static testcase *thiscase; |
| static struct flock flock; |
| static int parent, child, status, fail = 0; |
| static int got1 = 0; |
| static int fd; |
| static int test; |
| static char tmpname[40]; |
| |
| #define FILEDATA "ten bytes!" |
| |
| extern void catch1(); /* signal catching subroutine */ |
| extern void catch_alarm(); |
| |
| char *TCID = "fcntl14"; |
| int TST_TOTAL = 1; |
| |
| #ifdef UCLINUX |
| static char *argv0; /* Set by main(), passed to self_exec() */ |
| #endif |
| |
| /* |
| * cleanup() |
| * performs all the ONE TIME cleanup for this test at completion or |
| * premature exit |
| */ |
| void cleanup(void) |
| { |
| /* |
| * print timing status if that option was specified |
| * print errno log if that option was specified |
| */ |
| TEST_CLEANUP; |
| |
| tst_rmdir(); |
| |
| } |
| |
| /* |
| * setup |
| * performs all ONE TIME setup for this test |
| */ |
| void setup(void) |
| { |
| struct sigaction act; |
| |
| tst_sig(FORK, DEF_HANDLER, cleanup); |
| signal(SIGHUP, SIG_IGN); |
| umask(0); |
| TEST_PAUSE; |
| tst_tmpdir(); /* make temp dir and cd to it */ |
| parent = getpid(); |
| |
| /* setup temporary file name */ |
| sprintf(tmpname, "fcntl2.%d", parent); |
| |
| /* setup signal handler for signal from child */ |
| memset(&act, 0, sizeof(act)); |
| act.sa_handler = catch1; |
| sigemptyset(&act.sa_mask); |
| sigaddset(&act.sa_mask, SIGUSR1); |
| if ((sigaction(SIGUSR1, &act, NULL)) < 0) { |
| tst_resm(TFAIL, "SIGUSR1 signal setup failed, errno = %d", |
| errno); |
| cleanup(); |
| } |
| |
| memset(&act, 0, sizeof(act)); |
| act.sa_handler = catch_alarm; |
| sigemptyset(&act.sa_mask); |
| sigaddset(&act.sa_mask, SIGALRM); |
| if ((sigaction(SIGALRM, &act, NULL)) < 0) { |
| tst_resm(TFAIL, "SIGALRM signal setup failed"); |
| cleanup(); |
| } |
| } |
| |
| void wake_parent(void) |
| { |
| if ((kill(parent, SIGUSR1)) < 0) { |
| tst_resm(TFAIL, "Attempt to send signal to parent " "failed"); |
| tst_resm(TFAIL, "Test case %d, errno = %d", test + 1, errno); |
| fail = 1; |
| } |
| } |
| |
| void do_usleep_child() |
| { |
| usleep(100000); /* XXX how long is long enough? */ |
| wake_parent(); |
| exit(0); |
| } |
| |
| void dochild() |
| { /* child process */ |
| int rc; |
| pid_t pid; |
| |
| /* Initialize the child lock structure */ |
| flock.l_type = thiscase->c_type; |
| flock.l_whence = thiscase->c_whence; |
| flock.l_start = thiscase->c_start; |
| flock.l_len = thiscase->c_len; |
| flock.l_pid = 0; |
| fail = 0; |
| |
| /* |
| * Check to see if child lock will succeed. If it will, FLOCK |
| * structure will return with l_type changed to F_UNLCK. If it will |
| * not, the parent pid will be returned in l_pid and the type of |
| * lock that will block it in l_type. |
| */ |
| if ((rc = fcntl(fd, F_GETLK, &flock)) < 0) { |
| tst_resm(TFAIL, "Attempt to check lock status failed"); |
| tst_resm(TFAIL, "Test case %d, errno = %d", test + 1, errno); |
| fail = 1; |
| } else { |
| |
| if ((thiscase->c_flag) == NOBLOCK) { |
| if (flock.l_type != F_UNLCK) { |
| tst_resm(TFAIL, |
| "Test case %d, GETLK: type = %d, " |
| "%d was expected", test + 1, |
| flock.l_type, F_UNLCK); |
| fail = 1; |
| } |
| |
| if (flock.l_whence != thiscase->c_whence) { |
| tst_resm(TFAIL, |
| "Test case %d, GETLK: whence = %d, " |
| "should have remained %d", test + 1, |
| flock.l_whence, thiscase->c_whence); |
| fail = 1; |
| } |
| |
| if (flock.l_start != thiscase->c_start) { |
| tst_resm(TFAIL, |
| "Test case %d, GETLK: start = %" PRId64 |
| ", " "should have remained %" PRId64, |
| test + 1, (int64_t) flock.l_start, |
| (int64_t) thiscase->c_start); |
| fail = 1; |
| } |
| |
| if (flock.l_len != thiscase->c_len) { |
| tst_resm(TFAIL, |
| "Test case %d, GETLK: len = %" PRId64 |
| ", " "should have remained %" PRId64, |
| test + 1, (int64_t) flock.l_len, |
| (int64_t) thiscase->c_len); |
| fail = 1; |
| } |
| |
| if (flock.l_pid != 0) { |
| tst_resm(TFAIL, |
| "Test case %d, GETLK: pid = %d, " |
| "should have remained 0", test + 1, |
| flock.l_pid); |
| fail = 1; |
| } |
| } else { |
| if (flock.l_pid != parent) { |
| tst_resm(TFAIL, |
| "Test case %d, GETLK: pid = %d, " |
| "should be parent's id of %d", |
| test + 1, flock.l_pid, parent); |
| fail = 1; |
| } |
| |
| if (flock.l_type != thiscase->a_type) { |
| tst_resm(TFAIL, |
| "Test case %d, GETLK: type = %d, " |
| "should be parent's first lock type of %d", |
| test + 1, flock.l_type, |
| thiscase->a_type); |
| fail = 1; |
| } |
| } |
| } |
| |
| /* |
| * now try to set the lock, nonblocking |
| * This will succeed for NOBLOCK, |
| * fail for WILLBLOCK |
| */ |
| flock.l_type = thiscase->c_type; |
| flock.l_whence = thiscase->c_whence; |
| flock.l_start = thiscase->c_start; |
| flock.l_len = thiscase->c_len; |
| flock.l_pid = 0; |
| |
| if ((rc = fcntl(fd, F_SETLK, &flock)) < 0) { |
| if ((thiscase->c_flag) == NOBLOCK) { |
| tst_resm(TFAIL, "Attempt to set child NONBLOCKING " |
| "lock failed"); |
| tst_resm(TFAIL, "Test case %d, errno = %d", |
| test + 1, errno); |
| fail = 1; |
| } |
| } |
| |
| if ((thiscase->c_flag) == WILLBLOCK) { |
| /* Check for proper errno condition */ |
| if (rc != -1 || (errno != EACCES && errno != EAGAIN)) { |
| tst_resm(TFAIL, |
| "SETLK: rc = %d, errno = %d, -1/EAGAIN or EACCES " |
| "was expected", rc, errno); |
| fail = 1; |
| } |
| if (rc == 0) { |
| /* accidentally got the lock */ |
| /* XXX how to clean up? */ |
| (void)fcntl(fd, F_UNLCK, &flock); |
| } |
| /* |
| * Lock should succeed after blocking and parent releases |
| * lock, tell the parent to release the locks. |
| * Do the lock in this process, send the signal in a child |
| * process, so that the SETLKW actually uses the blocking |
| * mechanism in the kernel. |
| * |
| * XXX inherent race: we want to wait until the |
| * F_SETLKW has started, but we don't have a way to |
| * check that reliably in the child. (We'd |
| * need some way to have fcntl() atomically unblock a |
| * signal and wait for the lock.) |
| */ |
| pid = FORK_OR_VFORK(); |
| switch (pid) { |
| case -1: |
| tst_resm(TFAIL, "Fork failed"); |
| break; |
| case 0: /* child */ |
| #ifdef UCLINUX |
| if (self_exec(argv0, "nd", 1, parent) < 0) { |
| tst_resm(TFAIL, "self_exec failed"); |
| break; |
| } |
| #else |
| do_usleep_child(); |
| #endif |
| break; |
| |
| default: |
| if ((rc = fcntl(fd, F_SETLKW, &flock)) < 0) { |
| tst_resm(TFAIL, "Attempt to set child BLOCKING " |
| "lock failed"); |
| tst_resm(TFAIL, "Test case %d, errno = %d", |
| test + 1, errno); |
| fail = 1; |
| } |
| waitpid(pid, &status, 0); |
| break; |
| } |
| } |
| if (fail) { |
| exit(1); |
| } else { |
| exit(0); |
| } |
| } |
| |
| void run_test(int file_flag, int file_mode, int seek, int start, int end) |
| { |
| extern long time(); |
| |
| /* reset fail to 0 for each run_test call */ |
| fail = 0; |
| |
| /* loop thru all test cases */ |
| for (test = start; test < end; test++) { |
| /* open a temp file to lock */ |
| fd = open(tmpname, file_flag, file_mode); |
| if (fd < 0) { |
| tst_brkm(TBROK, cleanup, "open() failed"); |
| } |
| |
| /* write some dummy data to the file */ |
| if (write(fd, FILEDATA, 10) < 0) { |
| tst_brkm(TBROK, cleanup, "write() failed"); |
| } |
| |
| /* seek into file if indicated */ |
| if (seek) { |
| if (lseek(fd, seek, 0) < 0) { |
| tst_brkm(TBROK, cleanup, "lseek() failed"); |
| } |
| } |
| |
| /* Initialize first parent lock structure */ |
| thiscase = &testcases[test]; |
| flock.l_type = thiscase->a_type; |
| flock.l_whence = thiscase->a_whence; |
| flock.l_start = thiscase->a_start; |
| flock.l_len = thiscase->a_len; |
| |
| /* set the initial parent lock on the file */ |
| if ((fcntl(fd, F_SETLK, &flock)) < 0) { |
| tst_resm(TFAIL, "First parent lock failed"); |
| tst_resm(TFAIL, "Test case %d, errno = %d", |
| test + 1, errno); |
| fail = 1; |
| } |
| |
| if ((thiscase->b_type) != SKIP) { |
| /* Initialize second parent lock structure */ |
| flock.l_type = thiscase->b_type; |
| flock.l_whence = thiscase->b_whence; |
| flock.l_start = thiscase->b_start; |
| flock.l_len = thiscase->b_len; |
| |
| /* set the second parent lock */ |
| if ((fcntl(fd, F_SETLK, &flock)) < 0) { |
| tst_resm(TFAIL, "Second parent lock failed"); |
| tst_resm(TFAIL, "Test case %d, errno = %d", |
| test + 1, errno); |
| fail = 1; |
| } |
| } |
| if ((thiscase->c_type) == SKIP) { |
| /* close the temp file and move to next test case */ |
| close(fd); |
| tst_resm(TINFO, "skipping test %d", test + 1); |
| continue; /* continue to the next case */ |
| } |
| /* |
| * Mask SIG_USR1 before forking child, to avoid race |
| */ |
| (void)sighold(SIGUSR1); |
| |
| /* flush the stdout to avoid garbled output */ |
| fflush(stdout); |
| |
| /* spawn a child process */ |
| if ((child = FORK_OR_VFORK()) == 0) { |
| #ifdef UCLINUX |
| if (self_exec(argv0, "nddddddddd", 2, thiscase->c_type, |
| thiscase->c_whence, thiscase->c_start, |
| thiscase->c_len, thiscase->c_flag, |
| thiscase->a_type, fd, test, parent) < 0) { |
| tst_resm(TFAIL, "self_exec failed"); |
| cleanup(); |
| } |
| #else |
| dochild(); |
| #endif |
| } |
| if (child < 0) { |
| tst_resm(TFAIL, "Fork failed"); |
| cleanup(); |
| } |
| /* parent process */ |
| if ((thiscase->c_flag) == WILLBLOCK) { |
| /* |
| * Wait for a signal from the child then remove |
| * blocking lock. Set a 60 second alarm to break the |
| * pause just in case the child never signals us. |
| */ |
| alarm(TIME_OUT); |
| sigpause(SIGUSR1); |
| |
| /* turn off the alarm timer */ |
| alarm((unsigned)0); |
| if (got1 != 1) |
| tst_resm(TINFO, "Pause terminated without " |
| "signal SIGUSR1 from child"); |
| got1 = 0; /* reset the flag */ |
| |
| /* |
| * setup lock structure for parent to delete |
| * blocking lock then wait for child to exit |
| */ |
| flock.l_type = F_UNLCK; |
| flock.l_whence = 0; |
| flock.l_start = 0L; |
| flock.l_len = 0L; |
| if ((fcntl(fd, F_SETLK, &flock)) < 0) { |
| tst_resm(TFAIL, "Attempt to release parent " |
| "lock failed"); |
| tst_resm(TFAIL, "Test case %d, errno = %d", |
| test + 1, errno); |
| fail = 1; |
| } |
| } |
| /* |
| * set a 60 second alarm to break the wait just in case the |
| * child doesn't terminate on its own accord |
| */ |
| alarm(TIME_OUT); |
| |
| /* wait for the child to terminate and close the file */ |
| waitpid(child, &status, 0); |
| /* turn off the alarm clock */ |
| alarm((unsigned)0); |
| if (status != 0) { |
| tst_resm(TFAIL, "tchild returned status 0x%x", status); |
| fail = 1; |
| } |
| close(fd); |
| if (fail) { |
| tst_resm(TFAIL, "testcase:%d FAILED", test + 1); |
| } else { |
| tst_resm(TPASS, "testcase:%d PASSED", test + 1); |
| } |
| } |
| unlink(tmpname); |
| } |
| |
| void catch_alarm() |
| { |
| /* |
| * Timer has runout and child has not signaled, need |
| * to kill off the child as it appears it will not |
| * on its own accord. Check that it is still around |
| * as it may have terminated abnormally while parent |
| * was waiting for SIGUSR1 signal from it. |
| */ |
| if (kill(child, 0) == 0) { |
| kill(child, SIGKILL); |
| perror("The child didnot terminate on its own accord"); |
| } |
| } |
| |
| void catch1() |
| { /* invoked on catching SIGUSR1 */ |
| struct sigaction act; |
| |
| /* |
| * Set flag to let parent know that child is ready to have lock |
| * removed |
| */ |
| memset(&act, 0, sizeof(act)); |
| act.sa_handler = catch1; |
| sigemptyset(&act.sa_mask); |
| sigaddset(&act.sa_mask, SIGUSR1); |
| sigaction(SIGUSR1, &act, NULL); |
| got1++; |
| } |
| |
| int main(int ac, char **av) |
| { |
| int lc; |
| char *msg; |
| |
| if ((msg = parse_opts(ac, av, NULL, NULL)) != NULL) { |
| tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); |
| } |
| #ifdef UCLINUX |
| argv0 = av[0]; |
| |
| maybe_run_child(&do_usleep_child, "nd", 1, &parent); |
| thiscase = malloc(sizeof(testcase)); |
| |
| maybe_run_child(&dochild, "nddddddddd", 2, &thiscase->c_type, |
| &thiscase->c_whence, &thiscase->c_start, |
| &thiscase->c_len, &thiscase->c_flag, &thiscase->a_type, |
| &fd, &test, &parent); |
| #endif |
| |
| setup(); /* global setup */ |
| |
| if (tst_fs_type(cleanup, ".") == TST_NFS_MAGIC) { |
| tst_brkm(TCONF, cleanup, |
| "Cannot do fcntl on a file on NFS filesystem"); |
| } |
| |
| /* Check for looping state if -i option is given */ |
| for (lc = 0; TEST_LOOPING(lc); lc++) { |
| /* reset tst_count in case we are looping */ |
| tst_count = 0; |
| |
| /* //block1: */ |
| tst_resm(TINFO, "Enter block 1: without mandatory locking"); |
| fail = 0; |
| /* |
| * try various file locks on an ordinary file without |
| * mandatory locking |
| */ |
| (void)run_test(O_CREAT | O_RDWR | O_TRUNC, 0777, 0, 0, 36); |
| if (fail) { |
| tst_resm(TFAIL, "Block 1, test 1 FAILED"); |
| } else { |
| tst_resm(TPASS, "Block 1, test 1 PASSED"); |
| } |
| |
| /* Now try with negative values for L_start and L_len */ |
| (void)run_test(O_CREAT | O_RDWR | O_TRUNC, 0777, 5, 36, 45); |
| |
| if (fail) { |
| tst_resm(TFAIL, "Block 1, test 2 FAILED"); |
| } else { |
| tst_resm(TPASS, "Block 1, test 2 PASSED"); |
| } |
| |
| tst_resm(TINFO, "Exit block 1"); |
| |
| /* //block2: */ |
| tst_resm(TINFO, "Enter block 2: with mandatory locking"); |
| fail = 0; |
| /* |
| * Try various locks on a file with mandatory record locking |
| * this should behave the same as an ordinary file |
| */ |
| (void)run_test(O_CREAT | O_RDWR | O_TRUNC, S_ENFMT | S_IRUSR | |
| S_IWUSR, 0, 0, 36); |
| if (fail) { |
| tst_resm(TFAIL, "Block 2, test 1 FAILED"); |
| } else { |
| tst_resm(TPASS, "Block 2, test 1 PASSED"); |
| } |
| |
| /* Now try negative values for L_start and L_len */ |
| (void)run_test(O_CREAT | O_RDWR | O_TRUNC, S_ENFMT | S_IRUSR | |
| S_IWUSR, 5, 36, 45); |
| if (fail) { |
| tst_resm(TFAIL, "Block 2, test 2 FAILED"); |
| } else { |
| tst_resm(TPASS, "Block 2, test 2 PASSED"); |
| } |
| |
| tst_resm(TINFO, "Exit block 2"); |
| |
| /* //block3: */ |
| tst_resm(TINFO, "Enter block 3"); |
| fail = 0; |
| /* |
| * Check that proper error status is returned when invalid |
| * argument used for WHENCE (negative value) |
| */ |
| |
| /* open a temporary file to lock */ |
| fd = open(tmpname, O_CREAT | O_RDWR | O_TRUNC, 0777); |
| if (fd < 0) { |
| tst_brkm(TBROK, cleanup, "open failed"); |
| } |
| |
| /* Write some dummy data to the file */ |
| if (write(fd, FILEDATA, 10) < 0) { |
| tst_brkm(TBROK, cleanup, "write failed"); |
| } |
| |
| /* Initialize lock structure */ |
| flock.l_type = F_WRLCK; |
| flock.l_whence = -1; |
| flock.l_start = 0L; |
| flock.l_len = 0L; |
| |
| /* Set the lock on the file */ |
| if ((fcntl(fd, F_SETLK, &flock)) < 0) { |
| if (errno != EINVAL) { |
| tst_resm(TFAIL, "Expected %d got %d", |
| EINVAL, errno); |
| fail = 1; |
| } |
| } else { |
| tst_resm(TFAIL, "Lock succeeded when it should have " |
| "failed"); |
| fail = 1; |
| } |
| |
| /* Close and remove temp file */ |
| close(fd); |
| unlink(tmpname); |
| |
| if (fail) { |
| tst_resm(TINFO, "Test with mandatory " |
| "locking FAILED"); |
| } else { |
| tst_resm(TINFO, "Test with mandatory " |
| "locking PASSED"); |
| } |
| tst_resm(TINFO, "Exit block 3"); |
| |
| /* //block4: */ |
| tst_resm(TINFO, "Enter block 4"); |
| fail = 0; |
| /* |
| * Check that a lock on end of file is still valid when |
| * additional data is appended to end of file and a new |
| * process attempts to lock new data |
| */ |
| /* open a temp file to lock */ |
| fd = open(tmpname, O_CREAT | O_RDWR | O_TRUNC, 0777); |
| if (fd < 0) { |
| tst_brkm(TBROK, cleanup, "open failed"); |
| } |
| |
| /* Write some dummy data to the file */ |
| if (write(fd, FILEDATA, 10) < 0) { |
| tst_brkm(TBROK, cleanup, "write failed"); |
| } |
| |
| /* Initialize first parent lock structure */ |
| thiscase = &testcases[58]; |
| flock.l_type = thiscase->a_type; |
| flock.l_whence = thiscase->a_whence; |
| flock.l_start = thiscase->a_start; |
| flock.l_len = thiscase->a_len; |
| |
| /* Set the initial parent lock on the file */ |
| if ((fcntl(fd, F_SETLK, &flock)) < 0) { |
| tst_resm(TFAIL, "First parent lock failed"); |
| tst_resm(TFAIL, "Test case %d, errno = %d", 58, errno); |
| fail = 1; |
| } |
| |
| /* Write some additional data to end of file */ |
| if (write(fd, FILEDATA, 10) < 0) { |
| tst_brkm(TBROK, cleanup, "write failed"); |
| } |
| |
| /* Mask signal to avoid race */ |
| if (sighold(SIGUSR1) < 0) { |
| tst_brkm(TBROK, cleanup, "sighold failed"); |
| } |
| |
| /* spawn a child process */ |
| if ((child = FORK_OR_VFORK()) == 0) { |
| #ifdef UCLINUX |
| if (self_exec(argv0, "nddddddddd", 2, thiscase->c_type, |
| thiscase->c_whence, thiscase->c_start, |
| thiscase->c_len, thiscase->c_flag, |
| thiscase->a_type, fd, test, parent) < 0) { |
| tst_resm(TFAIL, "self_exec failed"); |
| cleanup(); |
| } |
| #else |
| dochild(); |
| #endif |
| } |
| if (child < 0) { |
| tst_resm(TFAIL, "Fork failed"); |
| cleanup(); |
| } |
| |
| /* parent process */ |
| |
| /* |
| * Wait for a signal from the child then remove blocking lock. |
| * Set a 60 sec alarm to break the pause just in case the |
| * child doesn't terminate on its own accord |
| */ |
| (void)alarm(TIME_OUT); |
| |
| /* pause for the SIGUSR1 signal from child */ |
| (void)sigpause(SIGUSR1); |
| |
| /* turn off the alarm timer */ |
| (void)alarm((unsigned)0); |
| if (got1 != 1) { |
| tst_resm(TINFO, "Pause terminated without signal " |
| "SIGUSR1 from child"); |
| } |
| got1 = 0; /* reset flag */ |
| |
| /* |
| * Set up lock structure for parent to delete |
| * blocking lock then wait for child to exit |
| */ |
| flock.l_type = F_UNLCK; |
| flock.l_whence = 0; |
| flock.l_start = 0L; |
| flock.l_len = 0L; |
| if ((fcntl(fd, F_SETLK, &flock)) < 0) { |
| tst_resm(TFAIL, "Attempt to release parent lock " |
| "failed"); |
| tst_resm(TFAIL, "Test case %d, errno = %d", test + 1, |
| errno); |
| fail = 1; |
| } |
| |
| /* |
| * set a 60 sec alarm to break the wait just in case the |
| * child doesn't terminate on its own accord |
| */ |
| (void)alarm(TIME_OUT); |
| |
| /* wait for the child to terminate and close the file */ |
| waitpid(child, &status, 0); |
| if (WEXITSTATUS(status) != 0) { |
| fail = 1; |
| tst_resm(TFAIL, "child returned bad exit status"); |
| } |
| |
| /* turn off the alarm clock */ |
| (void)alarm((unsigned)0); |
| if (status != 0) { |
| tst_resm(TFAIL, "child returned status 0x%x", status); |
| fail = 1; |
| } |
| close(fd); |
| unlink(tmpname); |
| |
| if (fail) { |
| tst_resm(TINFO, "Test of locks on file FAILED"); |
| } else { |
| tst_resm(TINFO, "Test of locks on file PASSED"); |
| } |
| tst_resm(TINFO, "Exit block 4"); |
| } |
| cleanup(); |
| tst_exit(); |
| } |