#include <errno.h>
#include <string.h>
#include <signal.h>
+#include <limits.h>
#define HEX_2_ASC(x) ((x) > 9) ? (x)-10+'a' : (x)+'0'
#define FILE_SIZE 1024
#define SOCKET_CLOSE(S) (close(S))
#define INVALID_SOCKET -1
-#define O_BINARY 0
-
#define HANDLE int
#define INVALID_HANDLE -1
#define SEEK(H, O) (lseek(H, O, SEEK_SET))
#define ALLOC_ALIGNED(S) (memalign(65536, S))
#define FREE_ALIGNED(P) (free(P))
+#ifndef F_GETDELEG
+#define F_GETDELEG (1024 + 15)
+#define F_SETDELEG (1024 + 16)
+
+struct delegation {
+ uint32_t d_flags;
+ uint16_t d_type;
+ uint16_t __pad;
+};
+#endif
+
static char *prog;
static char *filename = 0;
static int debug = 0;
static int testnumber = -1;
static int saved_errno = 0;
static int got_sigio = 0;
+static int lease_is_deleg = 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 *child[] = { "child0", "child1" };
+
#define CMD_WRLOCK 0
#define CMD_RDLOCK 1
#define CMD_UNLOCK 2
#define CMD_SIGIO 9
#define CMD_WAIT_SIGIO 10
#define CMD_TRUNCATE 11
-
-#define PASS 1
-#define FAIL 0
+#define CMD_GETDELEG 12
+#define CMD_SETDELEG 13
+#define CMD_CREATE 14
+#define CMD_UNLINK 15
+#define CMD_RENAME 16
+#define CMD_SYMLINK 17
+#define CMD_MKNOD 18
+#define CMD_CHMOD 19
+#define CMD_MKDIR 20
+#define CMD_RMDIR 21
+
+#define PASS 0
+#define FAIL 1
#define SERVER 0
#define CLIENT 1
#define FLAGS 2 /* index 2 is also used for do_open() flag, see below */
#define ARG FLAGS /* Arguments for Lease operations */
#define TIME FLAGS /* Time for waiting on sigio */
+#define ARG2 3 /* second argument for dir operations */
static char *get_cmd_str(int cmd)
{
case CMD_SIGIO: return "Setup SIGIO"; break;
case CMD_WAIT_SIGIO: return "Wait for SIGIO"; break;
case CMD_TRUNCATE: return "Truncate"; break;
+ case CMD_SETDELEG: return "Set Delegation"; break;
+ case CMD_GETDELEG: return "Get Delegation"; break;
+ case CMD_CREATE: return "Create"; break;
+ case CMD_UNLINK: return "Remove"; break;
+ case CMD_RENAME: return "Rename"; break;
+ case CMD_SYMLINK: return "Symlink"; break;
+ case CMD_MKNOD: return "Mknod"; break;
+ case CMD_CHMOD: return "Chmod"; break;
+ case CMD_MKDIR: return "Mkdir"; break;
+ case CMD_RMDIR: return "Rmdir"; break;
}
return "unknown";
}
{0,0,0,0,0,CLIENT}
};
+char *dirdeleg_descriptions[] = {
+ /* 1 */"Take Read Lease",
+ /* 2 */"Write Lease Should Fail",
+ /* 3 */"Dir Lease Should Be Broken on Create",
+ /* 4 */"Dir Lease Should Be Broken on Unlink",
+ /* 5 */"Dir Lease Should Be Broken on Rename",
+ /* 6 */"Dir Lease Should Be Broken on Symlink",
+ /* 7 */"Dir Lease Should Be Broken on Mknod",
+ /* 8 */"Dir Lease Should Be Broken on Chmod",
+ /* 9 */"Dir Lease Should Be Broken on Mkdir",
+ /* 10 */"Dir Lease Should Be Broken on Rmdir",
+};
+
+static int64_t dirdeleg_tests[][6] =
+ /* test # Action [offset|flags|arg] length expected server/client */
+ /* [sigio_wait_time] */
+{
+ /* Various tests to exercise leases */
+
+/* SECTION 1: Simple verification of being able to take leases */
+ /* Take Read Lease, and release it */
+ {1, CMD_OPEN, O_DIRECTORY|O_RDONLY, 0, PASS, SERVER },
+ {1, CMD_SETDELEG, F_RDLCK, 0, PASS, SERVER },
+ {1, CMD_GETDELEG, F_RDLCK, 0, PASS, SERVER },
+ {1, CMD_SETDELEG, F_UNLCK, 0, PASS, SERVER },
+ {1, CMD_CLOSE, 0, 0, PASS, SERVER },
+
+ /* Write Lease should fail */
+ {2, CMD_OPEN, O_DIRECTORY|O_RDONLY, 0, PASS, SERVER },
+ {2, CMD_SETDELEG, F_WRLCK, 0, FAIL, SERVER },
+ {2, CMD_CLOSE, 0, 0, PASS, SERVER },
+
+ /* Get SIGIO when dir lease is broken by a create */
+ {3, CMD_OPEN, O_DIRECTORY|O_RDONLY, 0, PASS, SERVER },
+ {3, CMD_SETDELEG, F_RDLCK, 0, PASS, SERVER },
+ {3, CMD_GETDELEG, F_RDLCK, 0, PASS, SERVER },
+ {3, CMD_SIGIO, 0, 0, PASS, SERVER },
+ {3, CMD_CREATE, 0, 0, PASS, CLIENT },
+ {3, CMD_WAIT_SIGIO, 5, 0, PASS, SERVER },
+ {3, CMD_UNLINK, 0, 0, PASS, SERVER },
+ {3, CMD_CLOSE, 0, 0, PASS, SERVER },
+
+ /* Get SIGIO when dir lease is broken by unlink */
+ {4, CMD_OPEN, O_DIRECTORY|O_RDONLY, 0, PASS, SERVER },
+ {4, CMD_CREATE, 0, 0, PASS, SERVER },
+ {4, CMD_SETDELEG, F_RDLCK, 0, PASS, SERVER },
+ {4, CMD_GETDELEG, F_RDLCK, 0, PASS, SERVER },
+ {4, CMD_SIGIO, 0, 0, PASS, SERVER },
+ {4, CMD_UNLINK, 0, 0, PASS, CLIENT },
+ {4, CMD_WAIT_SIGIO, 5, 0, PASS, SERVER },
+ {4, CMD_CLOSE, 0, 0, PASS, SERVER },
+
+ /* Get SIGIO when dir lease is broken by rename */
+ {5, CMD_OPEN, O_DIRECTORY|O_RDONLY, 0, PASS, SERVER },
+ {5, CMD_CREATE, 0, 0, PASS, SERVER },
+ {5, CMD_SETDELEG, F_RDLCK, 0, PASS, SERVER },
+ {5, CMD_GETDELEG, F_RDLCK, 0, PASS, SERVER },
+ {5, CMD_SIGIO, 0, 0, PASS, SERVER },
+ {5, CMD_RENAME, 0, 1, PASS, CLIENT },
+ {5, CMD_WAIT_SIGIO, 5, 0, PASS, SERVER },
+ {5, CMD_UNLINK, 1, 0, PASS, CLIENT },
+ {5, CMD_CLOSE, 0, 0, PASS, SERVER },
+
+ /* Get SIGIO when dir lease is broken by symlink */
+ {6, CMD_OPEN, O_DIRECTORY|O_RDONLY, 0, PASS, SERVER },
+ {6, CMD_SETDELEG, F_RDLCK, 0, PASS, SERVER },
+ {6, CMD_GETDELEG, F_RDLCK, 0, PASS, SERVER },
+ {6, CMD_SIGIO, 0, 0, PASS, SERVER },
+ {6, CMD_SYMLINK, 0, 1, PASS, CLIENT },
+ {6, CMD_WAIT_SIGIO, 5, 0, PASS, SERVER },
+ {6, CMD_UNLINK, 0, 0, PASS, CLIENT },
+ {6, CMD_CLOSE, 0, 0, PASS, SERVER },
+
+ /* Get SIGIO when dir lease is broken by mknod */
+ {7, CMD_OPEN, O_DIRECTORY|O_RDONLY, 0, PASS, SERVER },
+ {7, CMD_SETDELEG, F_RDLCK, 0, PASS, SERVER },
+ {7, CMD_GETDELEG, F_RDLCK, 0, PASS, SERVER },
+ {7, CMD_SIGIO, 0, 0, PASS, SERVER },
+ {7, CMD_MKNOD, 0, 0, PASS, CLIENT },
+ {7, CMD_WAIT_SIGIO, 5, 0, PASS, SERVER },
+ {7, CMD_UNLINK, 0, 0, PASS, CLIENT },
+ {7, CMD_CLOSE, 0, 0, PASS, SERVER },
+
+ /* Get SIGIO when dir lease is broken by chmod */
+ {8, CMD_OPEN, O_DIRECTORY|O_RDONLY, 0, PASS, SERVER },
+ {8, CMD_SETDELEG, F_RDLCK, 0, PASS, SERVER },
+ {8, CMD_GETDELEG, F_RDLCK, 0, PASS, SERVER },
+ {8, CMD_SIGIO, 0, 0, PASS, SERVER },
+ {8, CMD_CHMOD, 0775, 0, PASS, CLIENT },
+ {8, CMD_WAIT_SIGIO, 5, 0, PASS, SERVER },
+ {8, CMD_CLOSE, 0, 0, PASS, SERVER },
+
+ /* Get SIGIO when dir lease is broken by mkdir */
+ {9, CMD_OPEN, O_DIRECTORY|O_RDONLY, 0, PASS, SERVER },
+ {9, CMD_SETDELEG, F_RDLCK, 0, PASS, SERVER },
+ {9, CMD_GETDELEG, F_RDLCK, 0, PASS, SERVER },
+ {9, CMD_SIGIO, 0, 0, PASS, SERVER },
+ {9, CMD_MKDIR, 0, 0, PASS, CLIENT },
+ {9, CMD_WAIT_SIGIO, 5, 0, PASS, SERVER },
+ {9, CMD_RMDIR, 0, 0, PASS, SERVER },
+ {9, CMD_CLOSE, 0, 0, PASS, SERVER },
+
+ /* Get SIGIO when dir lease is broken by rmdir */
+ {10, CMD_OPEN, O_DIRECTORY|O_RDONLY, 0, PASS, SERVER },
+ {10, CMD_MKDIR, 0, 0, PASS, SERVER },
+ {10, CMD_SETDELEG, F_RDLCK, 0, PASS, SERVER },
+ {10, CMD_GETDELEG, F_RDLCK, 0, PASS, SERVER },
+ {10, CMD_SIGIO, 0, 0, PASS, SERVER },
+ {10, CMD_RMDIR, 0, 0, PASS, CLIENT },
+ {10, CMD_WAIT_SIGIO, 5, 0, PASS, SERVER },
+ {10, CMD_CLOSE, 0, 0, PASS, SERVER },
+
+ /* indicate end of array */
+ {0,0,0,0,0,SERVER},
+ {0,0,0,0,0,CLIENT}
+};
+
static struct {
int32_t test;
int32_t command;
void release_lease(int fd)
{
+ struct delegation deleg = { .d_type = F_UNLCK };
int rc;
- rc = fcntl(fd, F_SETLEASE, F_UNLCK);
+ if (lease_is_deleg)
+ rc = fcntl(fd, F_SETDELEG, &deleg);
+ else
+ rc = fcntl(fd, F_SETLEASE, F_UNLCK);
+
if (rc != 0)
fprintf(stderr, "%s Failed to remove lease %d : %d %s\n",
__FILE__, rc, errno, strerror(errno));
return (got_sigio ? PASS: FAIL);
}
+int create_directory(void)
+{
+ int ret;
+ struct stat st;
+
+ ret = mkdir(filename, 0777);
+ if (ret == 0)
+ return PASS;
+
+ if (errno != EEXIST) {
+ perror("directory create");
+ return FAIL;
+ }
+
+ ret = stat(filename, &st);
+ if (ret < 0) {
+ perror("stat");
+ return FAIL;
+ }
+
+ if (S_ISDIR(st.st_mode))
+ return PASS;
+
+ fprintf(stderr, "%s is not a directory\n", filename);
+ return FAIL;
+}
+
int do_open(int flag)
{
- int flags = flag|O_CREAT|O_BINARY;
+ int flags = flag;
+
+ if (!(flag & O_DIRECTORY))
+ flags |= O_CREAT;
if(debug > 1)
fprintf(stderr, "do_open %s 0x%x\n", filename, flags);
return PASS;
}
+int do_create(int idx)
+{
+ int fd;
+
+ fd = openat(f_fd, child[idx], O_WRONLY|O_CREAT, 0666);
+ if (fd < 0) {
+ perror("openat");
+ return FAIL;
+ }
+ close(fd);
+ return PASS;
+}
+
+int do_unlink(int idx)
+{
+ int ret;
+
+ ret = unlinkat(f_fd, child[idx], 0);
+ if (ret < 0) {
+ perror("unlink");
+ return FAIL;
+ }
+ return PASS;
+}
+
+int do_rename(int old, int new)
+{
+ int ret;
+
+ ret = renameat(f_fd, child[old], f_fd, child[new]);
+ if (ret < 0) {
+ perror("rename");
+ return FAIL;
+ }
+ return PASS;
+}
+
+int do_symlink(int path, int target)
+{
+ int ret;
+
+ ret = symlinkat(child[target], f_fd, child[path]);
+ if (ret < 0) {
+ perror("symlink");
+ return FAIL;
+ }
+ return PASS;
+}
+
+int do_mknod(int idx)
+{
+ int ret;
+
+ ret = mknodat(f_fd, child[idx], S_IFREG, 0);
+ if (ret < 0) {
+ perror("mknod");
+ return FAIL;
+ }
+ return PASS;
+}
+
+int do_mkdir(int idx)
+{
+ int ret;
+
+ ret = mkdirat(f_fd, child[idx], 0777);
+ if (ret < 0) {
+ perror("mkdir");
+ return FAIL;
+ }
+ return PASS;
+}
+
+int do_rmdir(int idx)
+{
+ int ret;
+
+ ret = unlinkat(f_fd, child[idx], AT_REMOVEDIR);
+ if (ret < 0) {
+ perror("mkdir");
+ return FAIL;
+ }
+ return PASS;
+}
+
+int do_chmod(int mode)
+{
+ int ret;
+
+ ret = chmod(filename, mode);
+ if (ret < 0) {
+ perror("chmod");
+ return FAIL;
+ }
+ return PASS;
+}
+
static int do_lock(int cmd, int type, int start, int length)
{
int ret;
errno = 0;
+ lease_is_deleg = 0;
ret = fcntl(f_fd, cmd, arg);
saved_errno = errno;
return(ret==0?PASS:FAIL);
}
+static int do_deleg(int cmd, unsigned short arg, int expected)
+{
+ struct delegation deleg = { .d_type = arg };
+ int ret;
+
+ if(debug > 1)
+ fprintf(stderr, "do_deleg: cmd=%d arg=%d exp=%X\n",
+ cmd, arg, expected);
+
+ if (f_fd < 0)
+ return f_fd;
+
+ errno = 0;
+
+ lease_is_deleg = 1;
+ ret = fcntl(f_fd, cmd, &deleg);
+ saved_errno = errno;
+
+ if (cmd == F_GETDELEG && ret == 0)
+ ret = deleg.d_type;
+
+ if (expected && (expected == ret))
+ ret = 0;
+
+ if(ret)
+ fprintf(stderr, "%s do_deleg: ret = %d, errno = %d (%s)\n",
+ __FILE__, ret, errno, strerror(errno));
+
+ return(ret==0?PASS:FAIL);
+}
+
int do_close(void)
{
if(debug > 1) {
{
int i, sts;
int c;
+ int openflags;
struct sockaddr_in myAddr;
struct linger noLinger = {1, 0};
char *host = NULL;
extern int optind;
int fail_count = 0;
int run_leases = 0;
+ int run_dirdelegs = 0;
int test_setlease = 0;
atexit(cleanup);
prog = p+1;
}
- while ((c = getopt(argc, argv, "dLn:h:p:t?")) != EOF) {
+ while ((c = getopt(argc, argv, "dDLn:h:p:t?")) != EOF) {
switch (c) {
case 'd': /* debug flag */
debug++;
break;
+ case 'D':
+ run_dirdelegs = 1;
+ break;
+
case 'L': /* Lease testing */
run_leases = 1;
break;
}
filename=argv[optind];
+
+ if (run_dirdelegs && create_directory() == FAIL)
+ exit(1);
+
if (debug)
fprintf(stderr, "Working on file : %s\n", filename);
- if (do_open(O_RDWR) == FAIL)
+
+ if (run_dirdelegs) {
+ openflags = O_RDONLY | O_DIRECTORY;
+ } else {
+ openflags = O_RDWR;
+ }
+
+ if (do_open(openflags) == FAIL)
exit(1);
if (test_setlease == 1) {
- fcntl(f_fd, F_SETLEASE, F_UNLCK);
+ struct delegation deleg = { .d_type = F_UNLCK };
+
+ if (run_dirdelegs)
+ fcntl(f_fd, F_SETDELEG, &deleg);
+ else
+ fcntl(f_fd, F_SETLEASE, F_UNLCK);
saved_errno = errno;
close(f_fd);
exit(saved_errno);
SRAND(6789L);
}
- if (server)
+ if (server && !run_dirdelegs)
/* only server need do shared file */
initialize(f_fd);
*
* real work is in here ...
*/
- if (run_leases)
+ if (run_dirdelegs)
+ fail_count = run(dirdeleg_tests, dirdeleg_descriptions);
+ else if (run_leases)
fail_count = run(lease_tests, lease_descriptions);
else
fail_count = run(lock_tests, lock_descriptions);
case CMD_TRUNCATE:
result = do_truncate(tests[index][OFFSET]);
break;
+ case CMD_SETDELEG:
+ result = do_deleg(F_SETDELEG, tests[index][ARG], 0);
+ break;
+ case CMD_GETDELEG:
+ result = do_deleg(F_GETDELEG, tests[index][ARG], tests[index][ARG]);
+ break;
+ case CMD_CREATE:
+ result = do_create(tests[index][ARG]);
+ break;
+ case CMD_UNLINK:
+ result = do_unlink(tests[index][ARG]);
+ break;
+ case CMD_RENAME:
+ result = do_rename(tests[index][ARG], tests[index][ARG2]);
+ break;
+ case CMD_SYMLINK:
+ result = do_symlink(tests[index][ARG], tests[index][ARG2]);
+ break;
+ case CMD_MKNOD:
+ result = do_mknod(tests[index][ARG]);
+ break;
+ case CMD_CHMOD:
+ result = do_chmod(tests[index][ARG]);
+ break;
+ case CMD_MKDIR:
+ result = do_mkdir(tests[index][ARG]);
+ break;
+ case CMD_RMDIR:
+ result = do_rmdir(tests[index][ARG]);
+ break;
}
if( result != tests[index][RESULT]) {
fail_flag++;
case CMD_TRUNCATE:
result = do_truncate(ctl.offset);
break;
+ case CMD_SETDELEG:
+ result = do_deleg(F_SETDELEG, ctl.offset, 0);
+ break;
+ case CMD_GETDELEG:
+ result = do_deleg(F_GETDELEG, ctl.offset, ctl.offset);
+ break;
+ case CMD_CREATE:
+ result = do_create(ctl.offset);
+ break;
+ case CMD_UNLINK:
+ result = do_unlink(ctl.offset);
+ break;
+ case CMD_RENAME:
+ result = do_rename(ctl.offset, ctl.length);
+ break;
+ case CMD_SYMLINK:
+ result = do_symlink(ctl.offset, ctl.length);
+ break;
+ case CMD_MKNOD:
+ result = do_mknod(ctl.offset);
+ break;
+ case CMD_CHMOD:
+ result = do_chmod(ctl.offset);
+ break;
+ case CMD_MKDIR:
+ result = do_mkdir(ctl.offset);
+ break;
+ case CMD_RMDIR:
+ result = do_rmdir(ctl.offset);
+ break;
}
if( result != ctl.result ) {
fprintf(stderr,"Failure in %d:%s\n",