/* * Copyright (c) 2000-2003 Silicon Graphics, Inc. * All Rights Reserved. * * 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. * * This program is distributed in the hope that it would 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 the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Synchronized byte range lock exerciser */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define HEX_2_ASC(x) ((x) > 9) ? (x)-10+'a' : (x)+'0' #define FILE_SIZE 1024 #define PLATFORM_INIT() /*no-op*/ #define PLATFORM_CLEANUP() /*no-op*/ #define LL "ll" extern int h_errno; #define inet_aton(STRING, INADDRP) \ (((INADDRP)->s_addr = inet_addr(STRING)) == -1 ? 0 : 1) /* this assumes 32 bit pointers */ #define PTR_TO_U64(P) ((unsigned __int64)(unsigned int)(P)) #define U64_TO_PTR(T,U) ((T)(void *)(unsigned int)(U)) #if __BYTE_ORDER == __LITTLE_ENDIAN #define bswap_uint16(x) (uint16_t)bswap_16(x) #define bswap_uint32(x) (uint32_t)bswap_32(x) #define bswap_uint64(x) (uint64_t)bswap_64(x) #else #define bswap_uint16(x) x #define bswap_uint32(x) x #define bswap_uint64(x) x #endif #define SOCKET int #define SOCKET_READ read #define SOCKET_WRITE write #define SOCKET_CLOSE(S) (close(S)) #define INVALID_SOCKET -1 #define O_BINARY 0 #define HANDLE int #define INVALID_HANDLE -1 #define OPEN(N,F) (open(N, F|O_CREAT|O_RDWR|O_BINARY| \ (D_flag ? O_DIRECT : 0), 0644)) #define SEEK(H, O) (lseek(H, O, SEEK_SET)) #define READ(H, B, L) (read(H, B, L)) #define WRITE(H, B, L) (write(H, B, L)) #define CLOSE(H) (close(H)) #define RAND() (rand()) #define SRAND(s) (srand(s)) #define SLEEP(s) (sleep(s)) #define MIN(A,B) (((A)<(B))?(A):(B)) #define MAX(A,B) (((A)>(B))?(A):(B)) #define ALLOC_ALIGNED(S) (memalign(65536, S)) #define FREE_ALIGNED(P) (free(P)) static char *prog; static char *filename = 0; static int debug = 0; static int server = 1; static int maxio = 8192; static int port = 7890; static int reopen=0; static int closed=0; static int testnumber = -1; static int saved_errno = 0; static SOCKET s_fd = -1; /* listen socket */ static SOCKET c_fd = -1; /* IPC socket */ static HANDLE f_fd = INVALID_HANDLE; /* shared file */ static char *buf; /* I/O buffer */ static int D_flag = 0; #define WRLOCK 0 #define RDLOCK 1 #define UNLOCK 2 #define F_CLOSE 3 #define F_OPEN 4 #define WAITRESPONSE 8 #define F_WAIT 16 #define PASS 1 #define FAIL 0 #define SERVER 0 #define CLIENT 1 #define TEST_NUM 0 #define COMMAND 1 #define OFFSET 2 #define LENGTH 3 #define RESULT 4 #define WHO 5 #define FLAGS 2 /* index 2 is also used for do_open() flag, see below */ /* * flags for Mac OS X do_open() * O_RDONLY 0x0000 * O_WRONLY 0x0001 * O_RDWR 0x0002 * O_NONBLOCK 0x0004 * O_APPEND 0x0008 * O_SHLOCK 0x0010 * O_EXLOCK 0x0020 * O_ASYNC 0x0040 * O_FSYNC 0x0080 * O_NOFOLLOW 0x0100 * O_CREAT 0x0200 * O_TRUNC 0x0400 * O_EXCL 0x0800 */ /* * When adding tests be sure to add to both the descriptions AND tests array. * Also, be sure to undo whatever is set for each test (eg unlock any locks) * There is no need to have a matching client command for each server command * (or vice versa) */ char *descriptions[] = { /* 1 */"Add a lock to an empty lock list", /* 2 */"Add a lock to the start and end of a list - no overlaps", /* 3 */"Add a lock to the middle of a list - no overlap", /* 4 */"Add different lock types to middle of the list - overlap exact match", /* 5 */"Add new lock which completely overlaps any old lock in the list", /* 6 */"Add new lock which itself is completely overlaped by any old lock in the list", /* 7 */"Add new lock which starts before any old lock in the list", /* 8 */"Add new lock which starts in the middle of any old lock in the list and ends after", /* 9 */"Add different new lock types which completely overlaps any old lock in the list", /* 10 */"Add different new locks which are completely overlaped by an old lock in the list", /* 11 */"Add different new lock types which start before the old lock in the list", /* 12 */"Add different new lock types which start in the middle of an old lock in the list and end after", /* 13 */"Add new lock, differing types and processes, to middle of the list - exact overlap match", /* 14 */"Add new lock, differing types and processes, which completely overlap any of the locks in the list", /* 15 */"Add new lock, differing types and processes, which are completely overlaped by locks in the list", /* 16 */"Add new lock, differing types and processes, which start before a lock in the list", /* 17 */"Add new lock, differing types and processes, which starts in the middle of a lock, and ends after", /* 18 */"Acquire write locks with overlapping ranges", /* 19 */"Acquire write locks with non-overlapping ranges extending beyond EOF", /* 20 */"Acquire write locks with overlapping ranges extending beyond EOF", /* 21 */"Acquire write locks on whole files", /* 22 */"Acquire write lock on whole file and range write lock", /* 23 */"Acquire read locks with non-overlapping ranges", /* 24 */"Acquire read locks with overlapping ranges", /* 25 */"Acquire read and write locks with no overlapping ranges", /* 26 */"Acquire read and write locks with overlapping ranges", /* 27 */"Acquire whole file write lock and then close without unlocking (and attempt a lock)", /* 28 */"Acquire two read locks, close and reopen the file, and test if the inital lock is still there", #if defined(macosx) /* 29 */"Close the opened file and open the file with SHLOCK, other client will try to open with SHLOCK too", /* 30 */"Close the opened file and open the file with SHLOCK, other client will try to open with EXLOCK", /* 31 */"Close the opened file and open the file with EXLOCK, other client will try to open with EXLOCK too", #endif /* 32 */"Test wait locking", /* 33 */"Test that 0,0 locks unlock everything", }; static int64_t tests[][6] = /* test # Action offset length expected server/client */ { /* Various simple tests exercising the list */ /* SECTION 1: WRITE and UNLOCK with the same process (SERVER) */ /* Add a lock to an empty list */ {1, WRLOCK, 1, 10, PASS, SERVER }, {1, UNLOCK, 1, 10, PASS, SERVER }, /* Add a lock to the start and end of a list - 1, 13 - no overlap */ {2, WRLOCK, 10, 10, PASS, SERVER }, {2, WRLOCK, 30, 10, PASS, SERVER }, {2, WRLOCK, 50, 10, PASS, SERVER }, {2, WRLOCK, 1, 5, PASS, SERVER }, {2, WRLOCK, 70, 5, PASS, SERVER }, {2, UNLOCK, 10, 10, PASS, SERVER }, {2, UNLOCK, 30, 10, PASS, SERVER }, {2, UNLOCK, 50, 10, PASS, SERVER }, {2, UNLOCK, 1, 5, PASS, SERVER }, {2, UNLOCK, 70, 5, PASS, SERVER }, /* Add a lock to the middle of a list - no overlap */ {3, WRLOCK, 10, 10, PASS, SERVER }, {3, WRLOCK, 30, 10, PASS, SERVER }, {3, WRLOCK, 50, 10, PASS, SERVER }, {3, WRLOCK, 42, 5, PASS, SERVER }, {3, UNLOCK, 10, 10, PASS, SERVER }, {3, UNLOCK, 30, 10, PASS, SERVER }, {3, UNLOCK, 50, 10, PASS, SERVER }, {3, UNLOCK, 42, 5, PASS, SERVER }, /* Add different lock types to middle of the list - overlap exact match - 3 */ {4, WRLOCK, 10, 10, PASS, SERVER }, {4, WRLOCK, 30, 10, PASS, SERVER }, {4, WRLOCK, 50, 10, PASS, SERVER }, /* Exact match - same lock type */ {4, WRLOCK, 30, 10, PASS, SERVER }, /* Exact match - different lock type */ {4, RDLOCK, 30, 10, PASS, SERVER }, /* Exact match - unlock */ {4, UNLOCK, 30, 10, PASS, SERVER }, /* New lock - as above, inserting in the list again */ {4, WRLOCK, 30, 10, PASS, SERVER }, {4, UNLOCK, 10, 10, PASS, SERVER }, {4, UNLOCK, 30, 10, PASS, SERVER }, {4, UNLOCK, 50, 10, PASS, SERVER }, /* Add new lock which completely overlaps any old lock in the list - 4,5,6 */ {5, WRLOCK, 10, 10, PASS, SERVER }, {5, WRLOCK, 30, 10, PASS, SERVER }, {5, WRLOCK, 50, 10, PASS, SERVER }, /* The start is the same, end overlaps */ {5, WRLOCK, 30, 15, PASS, SERVER }, /* The start is before, end is the same */ {5, WRLOCK, 25, 20, PASS, SERVER }, /* Both start and end overlap */ {5, WRLOCK, 22, 26, PASS, SERVER }, {5, UNLOCK, 10, 10, PASS, SERVER }, {5, UNLOCK, 22, 26, PASS, SERVER }, {5, UNLOCK, 50, 10, PASS, SERVER }, /* Add new lock which itself is completely overlaped by any old lock in the list - 7,8,10 */ {6, WRLOCK, 10, 10, PASS, SERVER }, {6, WRLOCK, 30, 10, PASS, SERVER }, {6, WRLOCK, 50, 10, PASS, SERVER }, /* The start is the same, end is in the middle of old lock - NOP */ {6, WRLOCK, 30, 5, PASS, SERVER }, /* The start and end are in the middle of old lock - NOP */ {6, WRLOCK, 32, 6, PASS, SERVER }, /* Start in the middle and end is the same - NOP */ {6, WRLOCK, 32, 8, PASS, SERVER }, {6, UNLOCK, 10, 10, PASS, SERVER }, {6, UNLOCK, 30, 10, PASS, SERVER }, {6, UNLOCK, 32, 8, PASS, SERVER }, {6, UNLOCK, 50, 10, PASS, SERVER }, /* Add new lock which starts before any old lock in the list - 2,9 */ {7, WRLOCK, 10, 10, PASS, SERVER }, {7, WRLOCK, 30, 10, PASS, SERVER }, {7, WRLOCK, 50, 10, PASS, SERVER }, /* Here is the new lock */ {7, WRLOCK, 27, 10, PASS, SERVER }, /* Go again with the end of the new lock matching the start of old lock */ {7, WRLOCK, 25, 2, PASS, SERVER }, {7, UNLOCK, 10, 10, PASS, SERVER }, {7, UNLOCK, 25, 15, PASS, SERVER }, {7, UNLOCK, 50, 10, PASS, SERVER }, /* Add new lock which starts in the middle of any old lock in the list and ends after - 11,12 */ {8, WRLOCK, 10, 10, PASS, SERVER }, {8, WRLOCK, 30, 10, PASS, SERVER }, {8, WRLOCK, 50, 10, PASS, SERVER }, /* Here is the new lock */ {8, WRLOCK, 35, 10, PASS, SERVER }, /* Go again with the end of the new lock matching the start of old lock */ {8, WRLOCK, 45, 2, PASS, SERVER }, {8, UNLOCK, 10, 10, PASS, SERVER }, {8, UNLOCK, 30, 17, PASS, SERVER }, {8, UNLOCK, 50, 10, PASS, SERVER }, /* SECTION 2: Overlapping READ and WRITE and UNLOCK with the same process (SERVER) */ /* Add different new lock types which completely overlaps any old lock in the list - 4,5,6 */ {9, WRLOCK, 10, 10, PASS, SERVER }, {9, WRLOCK, 30, 10, PASS, SERVER }, {9, WRLOCK, 50, 10, PASS, SERVER }, /* The start is the same, end overlaps */ {9, RDLOCK, 30, 15, PASS, SERVER }, /* The start is before, end is the same */ {9, WRLOCK, 25, 20, PASS, SERVER }, /* Both start and end overlap */ {9, RDLOCK, 22, 26, PASS, SERVER }, {9, UNLOCK, 10, 10, PASS, SERVER }, {9, UNLOCK, 22, 26, PASS, SERVER }, {9, UNLOCK, 50, 10, PASS, SERVER }, /* Add different new locks which are completely overlaped by an old lock in the list - 7,8,10 */ {10, WRLOCK, 10, 10, PASS, SERVER }, {10, WRLOCK, 30, 10, PASS, SERVER }, {10, WRLOCK, 50, 10, PASS, SERVER }, /* The start is the same, end is in the middle of old lock */ {10, RDLOCK, 30, 5, PASS, SERVER }, /* The start and end are in the middle of a lock */ {10, WRLOCK, 32, 2, PASS, SERVER }, /* Start in the middle and end is the same */ {10, RDLOCK, 36, 5, PASS, SERVER }, {10, UNLOCK, 10, 10, PASS, SERVER }, {10, UNLOCK, 30, 11, PASS, SERVER }, {10, UNLOCK, 50, 10, PASS, SERVER }, /* Add different new lock types which start before the old lock in the list - 2,9 */ {11, WRLOCK, 10, 10, PASS, SERVER }, {11, WRLOCK, 30, 10, PASS, SERVER }, {11, WRLOCK, 50, 10, PASS, SERVER }, /* Here is the new lock */ {11, RDLOCK, 27, 10, PASS, SERVER }, /* Go again with the end of the new lock matching the start of lock */ {11, WRLOCK, 25, 3, PASS, SERVER }, {11, UNLOCK, 10, 10, PASS, SERVER }, {11, UNLOCK, 25, 15, PASS, SERVER }, {11, UNLOCK, 50, 10, PASS, SERVER }, /* Add different new lock types which start in the middle of an old lock in the list and end after - 11,12 */ {12, WRLOCK, 10, 10, PASS, SERVER }, {12, WRLOCK, 30, 10, PASS, SERVER }, {12, WRLOCK, 50, 10, PASS, SERVER }, /* Here is the new lock */ {12, RDLOCK, 35, 10, PASS, SERVER }, /* Go again with the end of the new lock matching the start of old lock */ {12, WRLOCK, 44, 3, PASS, SERVER }, {12, UNLOCK, 10, 10, PASS, SERVER }, {12, UNLOCK, 30, 18, PASS, SERVER }, {12, UNLOCK, 50, 10, PASS, SERVER }, /* SECTION 3: Overlapping READ and WRITE and UNLOCK with the different processes (CLIENT/SERVER) */ /* Add new lock, differing types and processes, to middle of the list - exact overlap match - 3 */ {13, WRLOCK, 10, 10, PASS, SERVER }, {13, WRLOCK, 30, 10, PASS, SERVER }, {13, RDLOCK, 50, 10, PASS, SERVER }, /* Same lock type, different process */ {13, WRLOCK, 30, 10, FAIL, CLIENT }, {13, RDLOCK, 50, 10, PASS, CLIENT }, /* Exact match - different lock type, different process */ {13, RDLOCK, 30, 10, FAIL, CLIENT }, /* Exact match - unlock */ {13, UNLOCK, 30, 10, PASS, CLIENT }, /* New lock - as above, inserting in the list again */ {13, UNLOCK, 30, 10, PASS, SERVER }, /* Exact match - same lock type, different process */ {13, WRLOCK, 30, 10, PASS, CLIENT }, {13, UNLOCK, 10, 10, PASS, SERVER }, {13, UNLOCK, 30, 10, PASS, CLIENT }, {13, UNLOCK, 50, 10, PASS, SERVER }, /* Add new lock, differing types and processes, which completely overlap any of the locks in the list - 4,5,6 */ {14, WRLOCK, 10, 10, PASS, SERVER }, {14, WRLOCK, 30, 10, PASS, SERVER }, {14, RDLOCK, 50, 10, PASS, SERVER }, /* The start is the same, end overlaps */ {14, RDLOCK, 30, 15, FAIL, CLIENT }, {14, WRLOCK, 30, 15, FAIL, CLIENT }, /* The start is before, end is the same */ {14, RDLOCK, 25, 20, FAIL, CLIENT }, {14, WRLOCK, 25, 20, FAIL, CLIENT }, /* Both start and end overlap */ {14, RDLOCK, 22, 26, FAIL, CLIENT }, {14, WRLOCK, 22, 26, FAIL, CLIENT }, /* The start is the same, end overlaps */ {14, RDLOCK, 50, 15, PASS, CLIENT }, {14, WRLOCK, 50, 17, FAIL, CLIENT }, /* The start is before, end is the same */ {14, RDLOCK, 45, 20, PASS, CLIENT }, {14, WRLOCK, 43, 22, FAIL, CLIENT }, /* Both start and end overlap */ {14, RDLOCK, 42, 26, PASS, CLIENT }, {14, WRLOCK, 41, 28, FAIL, CLIENT }, {14, UNLOCK, 10, 10, PASS, SERVER }, {14, UNLOCK, 22, 26, PASS, SERVER }, {14, UNLOCK, 42, 26, PASS, CLIENT }, /* Add new lock, differing types and processes, which are completely overlaped by an old lock in the list - 7,8,10 */ {15, WRLOCK, 10, 10, PASS, SERVER }, {15, RDLOCK, 30, 10, PASS, SERVER }, {15, WRLOCK, 50, 10, PASS, SERVER }, /* The start is the same, end is in the middle of old lock */ {15, RDLOCK, 50, 5, FAIL, CLIENT }, {15, WRLOCK, 50, 5, FAIL, CLIENT }, /* The start and end are in the middle of old lock */ {15, RDLOCK, 52, 6, FAIL, CLIENT }, {15, WRLOCK, 52, 6, FAIL, CLIENT }, /* Start in the middle and end is the same */ {15, RDLOCK, 52, 8, FAIL, CLIENT }, {15, WRLOCK, 52, 8, FAIL, CLIENT }, /* The start is the same, end is in the middle of old lock */ {15, RDLOCK, 30, 5, PASS, CLIENT }, {15, WRLOCK, 30, 5, FAIL, CLIENT }, /* The start and end are in the middle of old lock */ {15, RDLOCK, 32, 6, PASS, CLIENT }, {15, WRLOCK, 32, 6, FAIL, CLIENT }, /* Start in the middle and end is the same */ {15, RDLOCK, 32, 8, PASS, CLIENT }, {15, WRLOCK, 32, 8, FAIL, CLIENT }, {15, UNLOCK, 10, 10, PASS, SERVER }, {15, UNLOCK, 30, 10, PASS, SERVER }, {15, UNLOCK, 50, 10, PASS, SERVER }, /* Add new lock, differing types and processes, which start before a lock in the list - 2,9 */ {16, RDLOCK, 10, 10, PASS, SERVER }, {16, WRLOCK, 50, 10, PASS, SERVER }, /* Start is before, end is the start of the old lock in list */ {16, RDLOCK, 5, 6, PASS, CLIENT }, {16, WRLOCK, 5, 6, FAIL, CLIENT }, /* Start is before, end is in the middle of the old lock */ {16, RDLOCK, 5, 10, PASS, CLIENT }, {16, WRLOCK, 5, 10, FAIL, CLIENT }, /* Start is before, end is the start of the old lock in list */ {16, RDLOCK, 45, 6, FAIL, CLIENT }, {16, WRLOCK, 45, 6, FAIL, CLIENT }, /* Start is before, end is in the middle of the old lock */ {16, RDLOCK, 45, 10, FAIL, CLIENT }, {16, WRLOCK, 45, 10, FAIL, CLIENT }, {16, UNLOCK, 5, 15, PASS, CLIENT }, {16, UNLOCK, 30, 10, PASS, SERVER }, {16, UNLOCK, 50, 10, PASS, SERVER }, /* Add new lock, differing types and processes, which starts in the middle of a lock, and ends after - 11,12 */ {17, WRLOCK, 10, 10, PASS, SERVER }, {17, RDLOCK, 30, 10, PASS, SERVER }, {17, WRLOCK, 50, 10, PASS, SERVER }, /* Start in the middle, end after lock in list */ {17, WRLOCK, 35, 10, FAIL, CLIENT }, /* Start matches end of lock in list */ {17, RDLOCK, 35, 10, PASS, CLIENT }, {17, RDLOCK, 44, 2, PASS, CLIENT }, /* Start in the middle, end after lock in list */ {17, RDLOCK, 55, 10, FAIL, CLIENT }, {17, WRLOCK, 55, 10, FAIL, CLIENT }, /* Start matches end of lock in list */ {17, RDLOCK, 59, 5, FAIL, CLIENT }, {17, WRLOCK, 59, 5, FAIL, CLIENT }, {17, UNLOCK, 10, 10, PASS, SERVER }, {17, UNLOCK, 30, 16, PASS, CLIENT }, {17, UNLOCK, 50, 10, PASS, SERVER }, /* SECTION 4: overlapping and EOF tests */ /* Acquire overlapping ranges */ {18, WRLOCK, 11, 7, PASS, SERVER }, {18, WRLOCK, 13, 8, FAIL, CLIENT }, {18, UNLOCK, 11, 7, PASS, SERVER }, /* Acquire different ranges beyond EOF */ {19, WRLOCK, 10, FILE_SIZE, PASS, SERVER }, {19, WRLOCK, FILE_SIZE + 10, 10, PASS, CLIENT }, {19, UNLOCK, 10, FILE_SIZE, PASS, SERVER }, {19, UNLOCK, FILE_SIZE + 10, 10, PASS, CLIENT }, /* Acquire same range beyong EOF */ {20, WRLOCK, 10, FILE_SIZE, PASS, SERVER, }, {20, WRLOCK, 10, FILE_SIZE, FAIL, CLIENT, }, {20, UNLOCK, 10, FILE_SIZE, PASS, SERVER, }, /* Acquire whole file lock */ {21, WRLOCK, 0, 0, PASS, SERVER, }, {21, WRLOCK, 0, 0, FAIL, CLIENT, }, {21, UNLOCK, 0, 0, PASS, SERVER, }, /* Acquire whole file lock, then range */ {22, WRLOCK, 0, 0, PASS, SERVER, }, {22, WRLOCK, 1, 5, FAIL, CLIENT, }, {22, UNLOCK, 0, 0, PASS, SERVER, }, /* Acquire non-overlapping read locks */ {23, RDLOCK, 1, 5, PASS, SERVER, }, {23, RDLOCK, 7, 6, PASS, CLIENT, }, {23, UNLOCK, 1, 5, PASS, SERVER, }, {23, UNLOCK, 7, 6, PASS, CLIENT, }, /* Acquire overlapping read locks */ {24, RDLOCK, 1, 5, PASS, SERVER, }, {24, RDLOCK, 2, 6, PASS, CLIENT, }, {24, UNLOCK, 1, 5, PASS, SERVER, }, {24, UNLOCK, 1, 7, PASS, CLIENT, }, /* Acquire non-overlapping read and write locks */ {25, RDLOCK, 1, 5, PASS, SERVER, }, {25, WRLOCK, 7, 6, PASS, CLIENT, }, {25, UNLOCK, 1, 5, PASS, SERVER, }, {25, UNLOCK, 7, 6, PASS, CLIENT, }, /* Acquire overlapping read and write locks */ {26, RDLOCK, 1, 5, PASS, SERVER, }, {26, WRLOCK, 2, 6, FAIL, CLIENT, }, {26, UNLOCK, 1, 5, PASS, SERVER, }, /* Acquire whole file lock, then close (without unlocking) */ {27, WRLOCK, 0, 0, PASS, SERVER, }, {27, WRLOCK, 1, 5, FAIL, CLIENT, }, {27, F_CLOSE,0, 0, PASS, SERVER, }, {27, WRLOCK, 1, 5, PASS, CLIENT, }, {27, F_OPEN, 0, 0, PASS, SERVER, }, {27, UNLOCK, 1, 5, PASS, CLIENT, }, /* Acquire two read locks, close one file and then reopen to check that first lock still exists */ {28, RDLOCK, 1, 5, PASS, SERVER, }, {28, RDLOCK, 1, 5, PASS, CLIENT, }, {28, F_CLOSE,0, 0, PASS, SERVER, }, {28, F_OPEN, 0, 0, PASS, SERVER, }, {28, WRLOCK, 0, 0, FAIL, SERVER, }, {28, UNLOCK, 1, 5, PASS, SERVER, }, {28, UNLOCK, 1, 5, PASS, CLIENT, }, #ifdef macosx /* Close the opened file and open the file with SHLOCK, other client will try to open with SHLOCK too */ {29, F_CLOSE,0, 0, PASS, SERVER, }, {29, F_OPEN, O_SHLOCK|O_NONBLOCK, 0, PASS, SERVER, }, {29, F_CLOSE,0, 0, PASS, CLIENT, }, {29, F_OPEN, O_SHLOCK|O_NONBLOCK, 0, PASS, CLIENT, }, /* Close the opened file and open the file with SHLOCK, other client will try to open with EXLOCK */ {30, F_CLOSE,0, 0, PASS, SERVER, }, {30, F_CLOSE,0, 0, PASS, CLIENT, }, {30, F_OPEN, O_SHLOCK|O_NONBLOCK, 0, PASS, SERVER, }, {30, F_OPEN, O_EXLOCK|O_NONBLOCK, 0, FAIL, CLIENT, }, /* Close the opened file and open the file with EXLOCK, other client will try to open with EXLOCK too */ {31, F_CLOSE,0, 0, PASS, SERVER, }, {31, F_CLOSE,0, 0, FAIL, CLIENT, }, {31, F_OPEN, O_EXLOCK|O_NONBLOCK, 0, PASS, SERVER, }, {31, F_OPEN, O_EXLOCK|O_NONBLOCK, 0, FAIL, CLIENT, }, {31, F_CLOSE,0, 0, PASS, SERVER, }, {31, F_CLOSE,0, 0, FAIL, CLIENT, }, {31, F_OPEN, 0, 0, PASS, SERVER, }, {31, F_OPEN, 0, 0, PASS, CLIENT, }, #endif /* macosx */ /* Do some wait lock tests */ {32, WRLOCK, 0, 10, PASS, SERVER,}, {32, WRLOCK|F_WAIT, 0, 10, PASS, CLIENT,}, {32, UNLOCK, 0, 10, PASS, SERVER,}, {32, WAITRESPONSE, 0, 0, PASS, CLIENT,}, {32, WRLOCK, 0, 10, FAIL, SERVER,}, {32, WRLOCK, 0, 10, PASS, CLIENT,}, {32, WRLOCK, 15, 10, PASS, SERVER,}, {32, UNLOCK, 0, 10, PASS, CLIENT,}, {32, WRLOCK, 0, 25, FAIL, CLIENT,}, /* Check that it works even if part is unlocked */ {32, WRLOCK|F_WAIT, 0, 25, PASS, CLIENT,}, {32, UNLOCK, 15, 10, PASS, SERVER,}, {32, WAITRESPONSE, 0, 0, PASS, CLIENT,}, {32, UNLOCK, 0, 25, PASS, CLIENT,}, /* Check that it locks right away if there's not a conflicting lock */ {32, WRLOCK|F_WAIT, 0, 25, PASS, CLIENT,}, {32, WAITRESPONSE, 0, 0, PASS, CLIENT,}, {32, UNLOCK, 0, 25, PASS, CLIENT,}, /* Check that 0,0 unlocks unlock everything */ {33, WRLOCK, 1, 5, PASS, SERVER, }, {33, WRLOCK, 10, 5, PASS, SERVER, }, {33, WRLOCK, 20, 5, PASS, SERVER, }, {33, WRLOCK, 30, 5, PASS, SERVER, }, {33, WRLOCK, 1, 5, FAIL, CLIENT, }, {33, WRLOCK, 10, 5, FAIL, CLIENT, }, {33, WRLOCK, 20, 5, FAIL, CLIENT, }, {33, WRLOCK, 30, 5, FAIL, CLIENT, }, {33, UNLOCK, 0, 0, PASS, SERVER, }, {33, WRLOCK, 1, 5, PASS, CLIENT, }, {33, WRLOCK, 10, 5, PASS, CLIENT, }, {33, WRLOCK, 20, 5, PASS, CLIENT, }, {33, WRLOCK, 30, 5, PASS, CLIENT, }, {33, UNLOCK, 0, 0, PASS, CLIENT, }, /* indicate end of array */ {0,0,0,0,0,SERVER}, {0,0,0,0,0,CLIENT} }; static struct { int32_t test; int32_t command; int64_t offset; int64_t length; int32_t result; int32_t index; int32_t error; int32_t padding; /* So mac and irix have the same size struct (bloody alignment) */ } ctl; void usage(void) { fprintf(stderr, "Usage: %s [options] sharedfile\n\ \n\ options:\n\ -p port TCP/IP port number for client-server communication\n\ -d enable debug tracing\n\ -n # test number to run\n\ -h host run as client and connect to server on remote host\n\ [default run as server]\n", prog); exit(1); } #define INIT_BUFSZ 512 void initialize(HANDLE fd) { char* ibuf; int j=0; int nwrite; int offset = 0; int togo = FILE_SIZE; if (D_flag) { ibuf = (char *)ALLOC_ALIGNED(INIT_BUFSZ); } else { ibuf = (char*)malloc(INIT_BUFSZ); } memset(ibuf, ':', INIT_BUFSZ); SEEK(fd, 0L); while (togo) { offset+=j; j = togo > INIT_BUFSZ ? INIT_BUFSZ : togo; if ((nwrite = WRITE(fd, ibuf, j)) != j) { if (nwrite < 0) perror("initialize write:"); else fprintf(stderr, "initialize: write() returns %d, not %d as expected\n", nwrite, j); exit(1); /*NOTREACHED*/ } togo -= j; } } int do_open(int flag) { if ((f_fd = OPEN(filename, flag)) == INVALID_HANDLE) { perror("shared file create"); if (!flag) /* Only exit if the first open fails */ exit(1); closed = 0; return FAIL; /*NOTREACHED*/ } closed = 0; #ifdef __sun if (D_flag) { directio(f_fd, DIRECTIO_ON); } #elif defined(__APPLE__) if (D_flag) { fcntl(f_fd, F_NOCACHE, 1); } #endif return PASS; } int do_lock(int type, int start, int length, int wait) { int ret; int filedes = f_fd; struct flock fl; if(debug > 1) { fprintf(stderr, "do_lock: start=%d, length=%d, wait=%d\n", start, length, wait); } if (f_fd < 0) return f_fd; fl.l_start = start; fl.l_len = length; fl.l_whence = SEEK_SET; fl.l_pid = getpid(); fl.l_type = type; errno = 0; if (wait) ret = fcntl(filedes, F_SETLKW, &fl); else ret = fcntl(filedes, F_SETLK, &fl); saved_errno = errno; if(debug > 1) fprintf(stderr, "do_lock: ret = %d, errno = %d (%s)\n", ret, errno, strerror(errno)); return(ret==0?PASS:FAIL); } int do_unlock(int start, int length) { int ret; int filedes = f_fd; struct flock fl; if(debug > 1) { fprintf(stderr, "do_unlock: start=%d, length=%d\n", start, length); } if (f_fd < 0) return f_fd; fl.l_start = start; fl.l_len = length; fl.l_whence = SEEK_SET; fl.l_pid = getpid(); fl.l_type = F_UNLCK; errno = 0; ret = fcntl(filedes, F_SETLK, &fl); saved_errno = errno; if(debug > 1 && ret) fprintf(stderr, "do_lock: ret = %d, errno = %d (%s)\n", ret, errno, strerror(errno)); return(ret==0?PASS:FAIL); } int do_close(void) { if(debug > 1) { fprintf(stderr, "do_close\n"); } errno =0; CLOSE(f_fd); saved_errno = errno; if (errno) return FAIL; return(PASS); } void send_ctl(void) { int nwrite; if (debug > 1) { fprintf(stderr, "send_ctl: test=%d, command=%d offset=%"LL"d, length=%"LL"d, result=%d, error=%d\n", ctl.test, ctl.command, (long long)ctl.offset, (long long)ctl.length,ctl.result, ctl.error); } ctl.test= bswap_uint32(ctl.test); ctl.command = bswap_uint32(ctl.command); ctl.offset = bswap_uint64(ctl.offset); ctl.length = bswap_uint64(ctl.length); ctl.result = bswap_uint32(ctl.result); ctl.index= bswap_uint32(ctl.index); ctl.error = bswap_uint32(ctl.error); nwrite = SOCKET_WRITE(c_fd, (char*)&ctl, sizeof(ctl)); ctl.test= bswap_uint32(ctl.test); ctl.command = bswap_uint32(ctl.command); ctl.offset = bswap_uint64(ctl.offset); ctl.length = bswap_uint64(ctl.length); ctl.result = bswap_uint32(ctl.result); ctl.index= bswap_uint32(ctl.index); ctl.error= bswap_uint32(ctl.error); if (nwrite != sizeof(ctl)) { if (nwrite < 0) perror("send_ctl: write"); else fprintf(stderr, "send_ctl[%d]: write() returns %d, not %zu as expected\n", ctl.test, nwrite, sizeof(ctl)); exit(1); /*NOTREACHED*/ } } void recv_ctl(void) { int nread; if ((nread = SOCKET_READ(c_fd, (char*)&ctl, sizeof(ctl))) != sizeof(ctl)) { if (nread < 0) perror("recv_ctl: read"); else { fprintf(stderr, "recv_ctl[%d]: read() returns %d, not %zu as expected\n", ctl.test, nread, sizeof(ctl)); fprintf(stderr, "socket might has been closed by other locktest\n"); } exit(1); /*NOTREACHED*/ } ctl.test= bswap_uint32(ctl.test); ctl.command = bswap_uint32(ctl.command); ctl.offset = bswap_uint64(ctl.offset); ctl.length = bswap_uint64(ctl.length); ctl.result = bswap_uint32(ctl.result); ctl.index= bswap_uint32(ctl.index); ctl.error= bswap_uint32(ctl.error); if (debug > 1) { fprintf(stderr, "recv_ctl: test=%d, command=%d offset=%"LL"d, length=%"LL"d, result=%d, error=%d\n", ctl.test, ctl.command, (long long)ctl.offset, (long long)ctl.length, ctl.result, ctl.error); } } void cleanup(void) { if (f_fd>=0 && !reopen && !closed) CLOSE(f_fd); if (c_fd>=0) SOCKET_CLOSE(c_fd); if (s_fd>=0) SOCKET_CLOSE(s_fd); PLATFORM_CLEANUP(); } int main(int argc, char *argv[]) { int i, sts; int c; struct sockaddr_in myAddr; struct linger noLinger = {1, 0}; char *host = NULL; char *endnum; int errflag = 0; char *p; extern char *optarg; extern int optind; extern int errno; int fail_count = 0; atexit(cleanup); PLATFORM_INIT(); /* trim command name of leading directory components */ prog = argv[0]; for (p = prog; *p; p++) { if (*p == '/') prog = p+1; } while ((c = getopt(argc, argv, "dn:h:p:?")) != EOF) { switch (c) { case 'd': /* debug flag */ debug++; break; case 'h': /* (server) hostname */ server = 0; host = optarg; break; case 'n': testnumber = atoi(optarg); break; case 'p': /* TCP/IP port */ port = (int)strtol(optarg, &endnum, 10); if (*endnum != '\0') { fprintf(stderr, "%s: -p argument must be a numeric\n", prog); exit(1); /*NOTREACHED*/ } break; case '?': default: errflag++; break; } } if (errflag || optind != argc-1) { usage(); /*NOTREACHED*/ } filename=argv[optind]; do_open(0); /* * +10 is slop for the iteration number if do_write() ... never * needed unless maxio is very small */ if (D_flag) { if ((buf = (char *)ALLOC_ALIGNED(maxio + 10)) == NULL) { perror("aligned alloc buf"); exit(1); /*NOTREACHED*/ } } else { if ((buf = (char *)malloc(maxio + 10)) == NULL) { perror("malloc buf"); exit(1); /*NOTREACHED*/ } } setbuf(stderr, NULL); if (server) { int one = 1; s_fd = socket(AF_INET, SOCK_STREAM, 0); if (s_fd == INVALID_SOCKET) { perror("socket"); exit(1); /*NOTREACHED*/ } if (setsockopt(s_fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof(one)) < 0) { perror("setsockopt(nodelay)"); exit(1); /*NOTREACHED*/ } if (setsockopt(s_fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one))<0) { perror("setsockopt(reuseaddr)"); exit(1); /*NOTREACHED*/ } #ifdef SO_REUSEPORT if (setsockopt(s_fd, SOL_SOCKET, SO_REUSEPORT, (char*)&one, sizeof(one))<0) { perror("setsockopt(reuseport)"); exit(1); /*NOTREACHED*/ } #endif memset(&myAddr, 0, sizeof(myAddr)); myAddr.sin_family = AF_INET; myAddr.sin_addr.s_addr = htonl(INADDR_ANY); myAddr.sin_port = htons((short)port); sts = bind(s_fd, (struct sockaddr*)&myAddr, sizeof(myAddr)); if (sts < 0) { perror("bind"); exit(1); /*NOTREACHED*/ } sts = listen(s_fd, 5); /* Max. of 5 pending connection requests */ if (sts == -1) { perror("listen"); exit(1); /*NOTREACHED*/ } c_fd = accept(s_fd, NULL, NULL); if (c_fd == INVALID_SOCKET) { perror("accept"); exit(1); /*NOTREACHED*/ } if (debug) fprintf(stderr, "Client accepted\n"); SRAND(12345L); } else { struct hostent *servInfo; if ((servInfo = gethostbyname(host)) == NULL) { printf("Couldn't get hostbyname for %s", host); if (h_errno == HOST_NOT_FOUND) printf(": host not found"); printf("\n"); exit(1); /*NOTREACHED*/ } c_fd = socket(AF_INET, SOCK_STREAM, 0); if (c_fd == INVALID_SOCKET) { perror("socket"); exit(1); /*NOTREACHED*/ } /* avoid 200 ms delay */ if (setsockopt(c_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&i, sizeof(i)) < 0) { perror("setsockopt(nodelay)"); exit(1); /*NOTREACHED*/ } /* Don't linger on close */ if (setsockopt(c_fd, SOL_SOCKET, SO_LINGER, (char *)&noLinger, sizeof(noLinger)) < 0) { perror("setsockopt(nolinger)"); exit(1); /*NOTREACHED*/ } memset(&myAddr, 0, sizeof(myAddr)); /* Arrgh! &myAddr, not myAddr */ myAddr.sin_family = AF_INET; memcpy(&myAddr.sin_addr, servInfo->h_addr, servInfo->h_length); myAddr.sin_port = htons((short)port); if (connect(c_fd, (struct sockaddr*)&myAddr, sizeof(myAddr)) < 0) { perror("unable to connect"); fprintf(stderr, "Server might still initializing the shared file\n "); exit(1); /*NOTREACHED*/ } if (debug) fprintf(stderr, "Connected to server\n"); SRAND(6789L); } if (server) /* only server need do shared file */ initialize(f_fd); /* * TCP/IP connection to be established, safe to proceed. * * real work is in here ... */ i = 0; { int index = 0; int end = 0; int result = 0; int last_test = 0; int test_count = 0; int fail_flag = 0; int command = 0; int wait = 0; while(!end) { if (server) { if(testnumber > 0) { last_test = testnumber - 1; while(tests[index][TEST_NUM] != testnumber && tests[index][TEST_NUM] != 0) { index++; } } /* If we have a server command, deal with it */ if(tests[index][WHO] == SERVER) { if(debug>1) fprintf(stderr, "Got a server command (%d)\n", index); if(tests[index][TEST_NUM] == 0) { index++; continue; } memset(&ctl, 0, sizeof(ctl)); ctl.test = tests[index][TEST_NUM]; if(tests[index][TEST_NUM] != 0) { command = tests[index][COMMAND]; wait = command & F_WAIT; command &= ~F_WAIT; switch(command) { case WRLOCK: result = do_lock(F_WRLCK, tests[index][OFFSET], tests[index][LENGTH], wait); break; case RDLOCK: result = do_lock(F_RDLCK, tests[index][OFFSET], tests[index][LENGTH], wait); break; case UNLOCK: result = do_unlock(tests[index][OFFSET], tests[index][LENGTH]); break; case F_CLOSE: result = do_close(); break; case F_OPEN: result = do_open(tests[index][FLAGS]); break; } if( result != tests[index][RESULT]) { fail_flag++; /* We have a failure */ if(debug) fprintf(stderr, "Server failure in test %d, while %sing using offset %lld, length %lld - err = %d:%s\n", ctl.test, tests[index][COMMAND]&~F_WAIT==WRLOCK?"write lock": tests[index][COMMAND]&~F_WAIT==RDLOCK?"read lock": tests[index][COMMAND]==UNLOCK?"unlock": tests[index][COMMAND]==F_OPEN?"open":"clos", (long long)tests[index][OFFSET], (long long)tests[index][LENGTH], saved_errno, strerror(saved_errno)); fprintf(stderr, "Server failure in %lld:%s\n", (long long)tests[index][TEST_NUM], descriptions[tests[index][TEST_NUM] - 1]); } } /* else send it off to the client */ } else if (tests[index][WHO] == CLIENT) { if(tests[index][TEST_NUM] == 0) { ctl.test = 0; end=1; } if(debug > 1) fprintf(stderr, "Sending command to client (%d) - %s - %lld:%lld\n", index, tests[index][COMMAND]&~F_WAIT==WRLOCK?"write lock": tests[index][COMMAND]&~F_WAIT==RDLOCK?"read lock": tests[index][COMMAND]==UNLOCK?"unlock": tests[index][COMMAND]==F_OPEN?"open":"clos", (long long)tests[index][OFFSET], (long long)tests[index][LENGTH]); /* get the client to do something */ ctl.index = index; send_ctl(); if(ctl.test != 0) { /* Get the clients response */ recv_ctl(); /* this is the whether the test passed or failed, * not what the command returned */ if( ctl.result == FAIL ) { fail_flag++; if(debug) fprintf(stderr, "Client failure in test %d, while %sing using offset %lld, length %lld - err = %d:%s\n", ctl.test, ctl.command&~F_WAIT==WRLOCK?"write lock": ctl.command&~F_WAIT==RDLOCK?"read lock": ctl.command==UNLOCK?"unlock": ctl.command==F_OPEN?"open":"clos", (long long)ctl.offset, (long long)ctl.length, ctl.error, strerror(ctl.error)); fprintf(stderr, "Client failure in %lld:%s\n", (long long)tests[index][TEST_NUM], descriptions[tests[index][TEST_NUM] - 1]); } } } if (debug > 1) { fprintf(stderr, "server sleeping ...\n"); SLEEP(1); } if(tests[index][TEST_NUM] != 0) { if(last_test != tests[index][TEST_NUM]) { test_count++; if(fail_flag) fail_count++; fail_flag = 0; } last_test = tests[index][TEST_NUM]; } index++; } else { /* CLIENT */ if(debug > 2) fprintf(stderr,"client: waiting...\n"); /* wait for the server to do something */ recv_ctl(); /* check for a client command */ index = ctl.index; if (tests[index][WHO] != CLIENT) { fprintf(stderr, "not a client command index (%d)\n", index); exit(1); } if(ctl.test == 0) { end = 1; break; } command = ctl.command = tests[index][COMMAND]; ctl.offset = tests[index][OFFSET]; ctl.length = tests[index][LENGTH]; wait = command & F_WAIT; command &= ~F_WAIT; if (wait) { ctl.result = PASS; send_ctl(); } switch(command) { case WRLOCK: result = do_lock(F_WRLCK, tests[index][OFFSET], tests[index][LENGTH], wait); break; case RDLOCK: result = do_lock(F_RDLCK, tests[index][OFFSET], tests[index][LENGTH], wait); break; case UNLOCK: result = do_unlock(tests[index][OFFSET], tests[index][LENGTH]); break; case F_CLOSE: result = do_close(); break; case F_OPEN: result = do_open(tests[index][FLAGS]); break; case WAITRESPONSE: //do nothing, result already stored properly break; } if( result != tests[index][RESULT] ) { if(debug) fprintf(stderr,"Got %d, wanted %lld\n", result, (long long)tests[index][RESULT]); ctl.result = FAIL; ctl.error = saved_errno; fail_count++; } else { ctl.result = PASS; } if(debug > 2) fprintf(stderr,"client: sending result to server (%d)\n", ctl.index); /* Send result to the server */ if (!wait) send_ctl(); if(tests[index][TEST_NUM] != 0) { if(last_test != tests[index][TEST_NUM]) test_count++; last_test = tests[index][TEST_NUM]; } } } if(server) printf("%d tests run, %d failed\n", test_count, fail_count); } if (buf) { if (D_flag) FREE_ALIGNED(buf); else free(buf); } exit(fail_count); /*NOTREACHED*/ }