[Ocfs2-test-devel] [PATCH 1/1] Ocfs2-test: Add destructive testcase for reflink_test v1.
Tristan Ye
tristan.ye at oracle.com
Mon Nov 2 03:15:47 PST 2009
Per sunil's request, we're going to add a destructive testcase for reflink
to expose an expected issue in existing reflink kernel codes: O_DIRECT writes
will not flush the metadata accordingly when being performed on reflinked files
after the completion of write operation, which means a reflinked file may wrongly
points to an old reflinked extent after a unexpectedly crash of machine.
The v1 testcase is really a quick&dirty one, but it did reveal the problem we
mentioned above. after doing reflinks, we fork procs to perform random writes
on a reflinked file, and each write is going to be logged accordingly via wire
to a remote listener server. then crash the machine somehow, afterwards, the
logfile recorded by listener will be used for verification.
I may file a bug on bugzilla to track the issue when tao get ready to change the
reflink kernel codes.
Signed-off-by: Tristan Ye <tristan.ye at oracle.com>
---
programs/reflink_tests/reflink_test.c | 198 +++++++++++++++++-
programs/reflink_tests/reflink_test.h | 28 +++
programs/reflink_tests/reflink_test_utils.c | 301 +++++++++++++++++++++++++++
3 files changed, 522 insertions(+), 5 deletions(-)
diff --git a/programs/reflink_tests/reflink_test.c b/programs/reflink_tests/reflink_test.c
index 9370925..cf16424 100755
--- a/programs/reflink_tests/reflink_test.c
+++ b/programs/reflink_tests/reflink_test.c
@@ -50,8 +50,13 @@ static char ref_path[PATH_MAX];
static char fh_log_orig[PATH_MAX];
static char fh_log_dest[PATH_MAX];
+static char dest_log_path[PATH_MAX];
+
+static char lsnr_addr[HOSTNAME_LEN];
+
static int iteration = 1;
static int testno = 1;
+static unsigned long port = 9999;
static unsigned long ref_counts = 10;
static unsigned long ref_trees = 10;
@@ -103,7 +108,7 @@ static void usage(void)
printf("Usage: reflink_tests [-i iteration] <-n ref_counts> "
"<-p refcount_tree_pairs> <-l file_size> <-d disk> "
"<-w workplace> -f -b [-c conc_procs] -m -s -r [-x xattr_nums]"
- " [-h holes_num] [-o holes_filling_log] -O -I\n\n"
+ " [-h holes_num] [-o holes_filling_log] -O -D -I\n\n"
"-f enable basic feature test.\n"
"-b enable boundary test.\n"
"-c enable concurrent tests with conc_procs processes.\n"
@@ -111,12 +116,16 @@ static void usage(void)
"-r enable random test.\n"
"-s enable stress test.\n"
"-O enable O_DIRECT test.\n"
+ "-D enable destructive test.\n"
+ "-v enable verification for destructive test.\n"
"-I enable inline-data test.\n"
"-x enable combination test with xattr.\n"
"-h enable holes punching and filling tests.\n"
"-o specify logfile for holes filling tests,it takes effect"
" when -h enabled.\n"
"-p specify number of refcount trees in fs.\n"
+ "-a specify listener's ip addr for destructive test.\n"
+ "-P specify listener's listening port for destructive test.\n"
"iteration specify the running times.\n"
"ref_counts specify the reflinks number for one shared inode.\n"
"refcount_tree_pairs specify the refcount tree numbers in fs.\n"
@@ -132,8 +141,8 @@ static int parse_opts(int argc, char **argv)
while (1) {
c = getopt(argc, argv,
- "i:d:w:IOfFbBsSrRmMW:n:N:"
- "l:L:c:C:p:P:x:X:h:H:o:");
+ "i:d:w:IODfFbBsSrRmMW:n:N:"
+ "l:L:c:C:p:x:X:h:H:o:v:a:P:");
if (c == -1)
break;
@@ -146,7 +155,6 @@ static int parse_opts(int argc, char **argv)
ref_counts = atol(optarg);
break;
case 'p':
- case 'P':
ref_trees = atol(optarg);
break;
case 'l':
@@ -159,6 +167,9 @@ static int parse_opts(int argc, char **argv)
case 'O':
test_flags |= ODCT_TEST;
break;
+ case 'D':
+ test_flags |= DSCV_TEST;
+ break;
case 'I':
test_flags |= INLN_TEST;
xattr_nums = 100;
@@ -170,6 +181,13 @@ static int parse_opts(int argc, char **argv)
case 'o':
strcpy(fh_log_orig, optarg);
break;
+ case 'v':
+ strcpy(dest_log_path, optarg);
+ test_flags |= VERI_TEST;
+ break;
+ case 'a':
+ strcpy(lsnr_addr, optarg);
+ break;
case 'f':
case 'F':
test_flags |= BASC_TEST;
@@ -204,6 +222,8 @@ static int parse_opts(int argc, char **argv)
case 'H':
test_flags |= HOLE_TEST;
hole_nums = atol(optarg);
+ case 'P':
+ port = atol(optarg);
default:
break;
}
@@ -215,6 +235,10 @@ static int parse_opts(int argc, char **argv)
if (strcmp(device, "") == 0)
return EINVAL;
+ if (test_flags & DSCV_TEST)
+ if (strcmp(lsnr_addr, "") == 0)
+ return EINVAL;
+
return 0;
}
@@ -890,7 +914,7 @@ static int concurrent_test()
signal(SIGCHLD, sigchld_handler);
- for (i = 0; i < child_nums; i++) {
+ for (i = 0; i < child_nums; i++) {
pid = fork();
@@ -1768,6 +1792,164 @@ static int holes_fill_test(void)
return 0;
}
+static int destructive_test(void)
+{
+ int ret, o_flags_rw, o_flags_ro, sockfd, i, j, status, rc;
+ int sub_testno = 1;
+ char log_rec[100], dest[PATH_MAX];
+
+ struct dest_write_unit du;
+
+ unsigned long align_slice = CHUNK_SIZE;
+ unsigned long align_filesz = align_slice;
+ unsigned long chunk_no = 0;
+
+ pid_t pid;
+
+ while (align_filesz < file_size)
+ align_filesz += CHUNK_SIZE;
+
+ chunk_no = file_size / CHUNK_SIZE;
+
+ printf("Test %d: Destructive reflink test.\n", testno);
+
+ o_flags_rw = open_rw_flags;
+ o_flags_ro = open_ro_flags;
+
+ open_rw_flags |= O_DIRECT;
+ open_ro_flags |= O_DIRECT;
+
+ snprintf(orig_path, PATH_MAX, "%s/original_destructive_refile", workplace);
+
+ printf(" *SubTest %d: Prepare original file in %ld chunks.\n",
+ sub_testno++, chunk_no);
+
+ ret = prep_orig_file_in_chunks(orig_path, chunk_no);
+ should_exit(ret);
+
+ printf(" *SubTest %d: Do reflinks to reflink the extents.\n",
+ sub_testno++);
+
+ ret = do_reflinks(orig_path, orig_path, ref_counts, 0);
+ should_exit(ret);
+
+ /*flush out the father's i/o buffer*/
+ fflush(stderr);
+ fflush(stdout);
+
+ signal(SIGCHLD, sigchld_handler);
+
+ printf(" *SubTest %d: Init socket for msg sending\n", sub_testno++);
+
+ sockfd = init_sock(lsnr_addr, port);
+
+ printf(" *SubTest %d: Fork children to write in chunks.\n", sub_testno++);
+
+ for (i = 0; i < child_nums; i++) {
+
+ pid = fork();
+
+ if (pid < 0) {
+ fprintf(stderr, "Fork process error!\n");
+ return pid;
+ }
+
+ /* child to do CoW*/
+ if (pid == 0) {
+
+ srand(getpid());
+
+ for (j = 0; j < chunk_no; j++) {
+ prep_rand_dest_write_unit(&du, get_rand(0, chunk_no - 1));
+ ret = do_write_chunk_file(orig_path, &du);
+ if (ret)
+ return -1;
+
+ memset(log_rec, 0, sizeof(log_rec));
+ snprintf(log_rec, sizeof(log_rec), "%lu\t%llu\t%c\n",
+ du.d_chunk_no, du.d_timestamp,
+ du.d_char);
+ write(sockfd, log_rec, strlen(log_rec) + 1);
+
+ /*
+ if (get_rand(0, 1)) {
+ snprintf(dest, PATH_MAX,
+ "%s_target_%d_%d",
+ orig_path, getpid(), j);
+ ret = reflink(orig_path, dest, 1);
+ should_exit(ret);
+ memset(log_rec, 0, 100);
+ snprintf(log_rec, 100, "Reflinking:\t%s->%s\n",orig_path, dest);
+ write(sockfd, log_rec, 100);
+
+ }
+ */
+ usleep(100000);
+ }
+
+ exit(0);
+ }
+
+ if (pid > 0)
+ child_pid_list[i] = pid;
+
+ }
+
+ usleep(100000 * 2);
+
+ /*
+ * Are you ready to crash the box?
+ */
+ system("echo b>/proc/sysrq-trigger");
+
+ signal(SIGINT, sigint_handler);
+ signal(SIGTERM, sigterm_handler);
+
+ /*father wait all children to leave*/
+ for (i = 0; i < child_nums; i++) {
+ ret = waitpid(child_pid_list[i], &status, 0);
+ rc = WEXITSTATUS(status);
+ if (rc) {
+ fprintf(stderr, "Child %d exits abnormally with "
+ "RC=%d\n", child_pid_list[i], rc);
+ }
+ }
+
+ open_rw_flags = o_flags_rw;
+ open_ro_flags = o_flags_ro;
+
+ //ret = do_unlinks(orig_path, ref_counts);
+ //should_exit(ret);
+
+ //ret = do_unlink(orig_path);
+ //should_exit(ret);
+
+ close(sockfd);
+
+ return 0;
+}
+
+static int verification_dest(void)
+{
+
+ unsigned long align_slice = CHUNK_SIZE;
+ unsigned long align_filesz = align_slice;
+ unsigned long chunk_no = 0;
+
+ while (align_filesz < file_size)
+ align_filesz += CHUNK_SIZE;
+
+ chunk_no = file_size / CHUNK_SIZE;
+
+ printf("Test %d: Verification for destructive test.\n", testno);
+
+ snprintf(orig_path, PATH_MAX, "%s/original_destructive_refile", workplace);
+
+ verify_dest_files(dest_log_path, orig_path, chunk_no);
+
+ return 0;
+}
+
static int directio_test(void)
{
@@ -2005,6 +2187,12 @@ static void run_test(void)
if (test_flags & INLN_TEST)
inline_test();
+ if (test_flags & DSCV_TEST)
+ destructive_test();
+
+ if (test_flags & VERI_TEST)
+ verification_dest();
+
}
}
diff --git a/programs/reflink_tests/reflink_test.h b/programs/reflink_tests/reflink_test.h
index 3f7b263..2179207 100755
--- a/programs/reflink_tests/reflink_test.h
+++ b/programs/reflink_tests/reflink_test.h
@@ -33,6 +33,7 @@
#include <sys/ioctl.h>
#include <inttypes.h>
#include <linux/types.h>
+#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
@@ -40,6 +41,11 @@
#include <assert.h>
#include <getopt.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
#include <ocfs2/ocfs2.h>
#define OCFS2_MAX_FILENAME_LEN 255
@@ -66,6 +72,8 @@
#define HOLE_TEST 0x00000200
#define ODCT_TEST 0x00000400
#define INLN_TEST 0x00000800
+#define DSCV_TEST 0x00001000
+#define VERI_TEST 0x00002000
#define MPI_RET_SUCCESS 0
#define MPI_RET_FAILED 1
@@ -74,12 +82,21 @@
#define RAND_CHAR_START 'A'
#define MAGIC_HOLE_CHAR (RAND_CHAR_START - 1)
+#define CHUNK_SIZE (1024*8)
+#define HOSTNAME_LEN 256
+
struct write_unit {
char w_char;
unsigned long w_offset;
unsigned int w_len;
};
+struct dest_write_unit{
+ unsigned long d_chunk_no;
+ unsigned long long d_timestamp;
+ char d_char;
+};
+
char rand_char(void);
unsigned long get_rand(unsigned long min, unsigned long max);
int get_rand_buf(char *buf, unsigned long size);
@@ -97,6 +114,7 @@ int mmap_write_at_file(char *pathname, const void *buf, size_t count,
int fill_pattern(unsigned long size);
int prep_orig_file(char *file_name, unsigned long size, int once);
int prep_orig_file_dio(char *file_name, unsigned long size);
+int prep_orig_file_in_chunks(char *file_name, unsigned long chunks);
int verify_pattern(char *buf, unsigned long offset, unsigned long size);
int verify_orig_file(char *orig);
@@ -124,4 +142,14 @@ void prep_rand_write_unit(struct write_unit *wu);
int do_write(int fd, struct write_unit *wu);
int do_write_file(char *fname, struct write_unit *wu);
+unsigned long long get_time_microseconds(void);
+void prep_rand_dest_write_unit(struct dest_write_unit *du,
+ unsigned long chunk_no);
+int fill_chunk_pattern(char *pattern, struct dest_write_unit dwu);
+int dump_pattern(char *pattern, struct dest_write_unit *dwu);
+int verify_chunk_pattern(char *pattern, struct dest_write_unit dwu);
+int do_write_chunk(int fd, struct dest_write_unit *du);
+int do_write_chunk_file(char *fname, struct dest_write_unit *du);
+int init_sock(char *serv, int port);
+int verify_dest_files(char *log, char *orig, unsigned long chunk_no);
#endif
diff --git a/programs/reflink_tests/reflink_test_utils.c b/programs/reflink_tests/reflink_test_utils.c
index 1401566..dd01030 100755
--- a/programs/reflink_tests/reflink_test_utils.c
+++ b/programs/reflink_tests/reflink_test_utils.c
@@ -41,6 +41,7 @@ extern struct ocfs2_super_block *ocfs2_sb;
extern char *prog;
static char buf_dio[DIRECTIO_SLICE] __attribute__ ((aligned(DIRECTIO_SLICE)));
+static char chunk_pattern[CHUNK_SIZE] __attribute__ ((aligned(DIRECTIO_SLICE)));
unsigned long get_rand(unsigned long min, unsigned long max)
{
@@ -464,6 +465,132 @@ int prep_orig_file_dio(char *file_name, unsigned long size)
return 0;
}
+int fill_chunk_pattern(char *pattern, struct dest_write_unit dwu)
+{
+ unsigned long mem_offset = 0;
+ unsigned long checksum = 0;
+
+ memset(pattern, 0, CHUNK_SIZE);
+ mem_offset = 0;
+
+ memmove(pattern , &dwu.d_chunk_no, sizeof(unsigned long));
+ mem_offset += sizeof(unsigned long);
+ memmove(pattern + mem_offset, &dwu.d_timestamp,
+ sizeof(unsigned long long ));
+ mem_offset += sizeof(unsigned long long);
+ memmove(pattern + mem_offset, &checksum, sizeof(unsigned long));
+ mem_offset += sizeof(unsigned long);
+
+ memset(pattern + mem_offset, dwu.d_char, CHUNK_SIZE - mem_offset * 2);
+ mem_offset = CHUNK_SIZE - mem_offset;
+
+ memmove(pattern + mem_offset, &checksum, sizeof(unsigned long));
+ mem_offset += sizeof(unsigned long);
+ memmove(pattern + mem_offset, &dwu.d_timestamp,
+ sizeof(unsigned long long ));
+ mem_offset += sizeof(unsigned long long);
+ memmove(pattern + mem_offset, &dwu.d_chunk_no, sizeof(unsigned long));
+
+ return 0;
+}
+
+int dump_pattern(char *pattern, struct dest_write_unit *dwu)
+{
+ unsigned long mem_offset = 0;
+ unsigned long checksum = 0;
+
+ memset(dwu, 0, sizeof(struct dest_write_unit));
+
+ memmove(&dwu->d_chunk_no, pattern, sizeof(unsigned long));
+ mem_offset += sizeof(unsigned long);
+ memmove(&dwu->d_timestamp, pattern + mem_offset,
+ sizeof(unsigned long long));
+ mem_offset += sizeof(unsigned long);
+ memmove(&checksum, pattern + mem_offset, sizeof(unsigned long));
+ mem_offset += sizeof(unsigned long);
+
+ memmove(&dwu->d_char, pattern + mem_offset, 1);
+ mem_offset = CHUNK_SIZE - mem_offset;
+
+ memmove(&checksum, pattern + mem_offset, sizeof(unsigned long));
+ mem_offset += sizeof(unsigned long);
+ memmove(&dwu->d_timestamp, pattern + mem_offset,
+ sizeof(unsigned long long));
+ mem_offset += sizeof(unsigned long long);
+ memmove(&dwu->d_chunk_no, pattern + mem_offset, sizeof(unsigned long));
+
+ return 0;
+}
+
+int verify_chunk_pattern(char *pattern, struct dest_write_unit dwu)
+{
+ char tmp_pattern[CHUNK_SIZE];
+
+ fill_chunk_pattern(tmp_pattern, dwu);
+
+ return !memcmp(pattern, tmp_pattern, sizeof(struct dest_write_unit));
+}
+
+int prep_orig_file_in_chunks(char *file_name, unsigned long chunks)
+{
+
+ int fd, ret, o_ret, flags;
+ unsigned long offset = 0;
+ unsigned long size = CHUNK_SIZE * chunks, chunk_no = 0;
+ struct dest_write_unit dwu;
+
+ if ((CHUNK_SIZE % DIRECTIO_SLICE) != 0) {
+
+ fprintf(stderr, "File size in destructive tests is expected to "
+ "be %d aligned, your chunk size %d is not allowed.\n",
+ DIRECTIO_SLICE, CHUNK_SIZE);
+ return -1;
+ }
+
+ flags = FILE_RW_FLAGS;
+
+ fd = open64(file_name, flags, FILE_MODE);
+
+ if (fd < 0) {
+ o_ret = fd;
+ fd = errno;
+ fprintf(stderr, "create file %s failed:%d:%s\n", file_name, fd,
+ strerror(fd));
+ fd = o_ret;
+ return fd;
+ }
+
+ /*
+ * Original file for desctrutive tests, it consists of chunks.
+ * Each chunks consists of following parts:
+ * chunkno + timestamp + checksum + random chars
+ * + checksum + timestamp + chunkno
+ *
+ */
+
+ while (offset < size) {
+
+ memset(&dwu, 0, sizeof(struct dest_write_unit));
+ dwu.d_chunk_no = chunk_no;
+ fill_chunk_pattern(chunk_pattern, dwu);
+
+ ret = pwrite(fd, chunk_pattern, CHUNK_SIZE, offset);
+ if (ret < 0) {
+ o_ret = ret;
+ ret = errno;
+ fprintf(stderr, "write failed:%d:%s\n", ret,
+ strerror(ret));
+ return ret;
+ }
+
+ chunk_no++;
+ offset += CHUNK_SIZE;
+ }
+
+ close(fd);
+ return 0;
+}
+
int verify_reflink_pair(const char *src, const char *dest)
{
int fds, fdd, ret, o_ret;
@@ -1279,3 +1406,177 @@ int do_write_file(char *fname, struct write_unit *wu)
return ret;
}
+
+unsigned long long get_time_microseconds(void)
+{
+ unsigned long long curtime_ms = 0;
+ struct timeval curtime;
+
+ gettimeofday(&curtime, NULL);
+
+ curtime_ms = (unsigned long long)curtime.tv_sec * 1000000 +
+ curtime.tv_usec;
+
+ return curtime_ms;
+}
+
+void prep_rand_dest_write_unit(struct dest_write_unit *du,
+ unsigned long chunk_no)
+{
+ du->d_char = rand_char();
+ du->d_chunk_no = chunk_no;
+ du->d_timestamp = get_time_microseconds();
+}
+
+int do_write_chunk(int fd, struct dest_write_unit *du)
+{
+ int ret;
+
+ fill_chunk_pattern(chunk_pattern, *du);
+
+ ret = pwrite(fd, chunk_pattern, CHUNK_SIZE, CHUNK_SIZE * du->d_chunk_no);
+ if (ret == -1) {
+ fprintf(stderr, "write error %d: \"%s\"\n", errno,
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+int do_write_chunk_file(char *fname, struct dest_write_unit *du)
+{
+ int fd, ret, o_ret, flags = open_rw_flags;
+
+ if (test_flags & DSCV_TEST)
+ flags |= O_DIRECT;
+
+ fd = open64(fname, flags);
+
+ if (fd < 0) {
+ o_ret = fd;
+ fd = errno;
+ fprintf(stderr, "open file %s failed:%d:%s\n", fname, fd,
+ strerror(fd));
+ fd = o_ret;
+ return fd;
+ }
+
+ ret = do_write_chunk(fd, du);
+
+ close(fd);
+
+ return ret;
+}
+
+
+int init_sock(char *serv, int port)
+{
+ int sockfd;
+ struct sockaddr_in servaddr;
+
+ sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ bzero(&servaddr, sizeof(struct sockaddr_in));
+ servaddr.sin_family = AF_INET;
+ servaddr.sin_port = htons(port);
+ inet_pton(AF_INET, serv, &servaddr.sin_addr);
+
+ connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
+
+ return sockfd;
+}
+
+int verify_dest_files(char *log, char *orig, unsigned long chunk_no)
+{
+ FILE *logfile;
+ struct dest_write_unit *dwus, dwu;
+ unsigned long i, t_bytes = sizeof(struct dest_write_unit) * chunk_no;
+ int fd = 0, ret = 0, o_ret;
+ char dest[PATH_MAX];
+
+ memset(&dwu, 0, sizeof(struct dest_write_unit));
+
+ dwus = (struct dest_write_unit *)malloc(t_bytes);
+ memset(dwus, 0, t_bytes);
+
+ logfile = fopen(log, "r");
+ if (!logfile) {
+ fprintf(stderr, "Error %d opening dest log: %s\n", errno,
+ strerror(errno));
+ ret = -EINVAL;
+ goto bail;
+ }
+
+ while (!feof(logfile)) {
+
+ ret = fscanf(logfile, "%lu\t%llu\t%c\n", &dwu.d_chunk_no,
+ &dwu.d_timestamp, &dwu.d_char);
+
+ if (ret != 3) {
+ fprintf(stderr, "input failure from dest log, ret "
+ "%d, %d %s\n", ret, errno, strerror(errno));
+ ret = -EINVAL;
+ goto bail;
+ }
+
+ if (dwu.d_timestamp >= dwus[dwu.d_chunk_no].d_timestamp) {
+
+ /*
+ printf("#%lu \tchunk record updated, from [%llu](%c) to [%llu](%c)\n",
+ dwu.d_chunk_no, dwus[dwu.d_chunk_no].d_timestamp, dwus[dwu.d_chunk_no].d_char,
+ dwu.d_timestamp, dwu.d_char);
+ */
+ memmove(&dwus[dwu.d_chunk_no], &dwu,
+ sizeof(struct dest_write_unit));
+ }
+
+ }
+
+ fd = open64(orig, open_ro_flags, FILE_MODE);
+ if (fd < 0) {
+ ret = fd;
+ fd = errno;
+ fprintf(stderr, "open file %s failed:%d:%s\n",
+ orig, fd, strerror(fd));
+ goto bail;
+ }
+
+ for (i = 0; i < chunk_no; i++) {
+
+ ret = pread(fd, chunk_pattern, CHUNK_SIZE, CHUNK_SIZE * i);
+ if (ret < 0) {
+ o_ret = ret;
+ ret = errno;
+ fprintf(stderr, "read failed:%d:%s\n", ret,
+ strerror(ret));
+ ret = o_ret;
+ goto bail;
+ }
+
+ if (!verify_chunk_pattern(chunk_pattern, dwus[i])) {
+
+ dump_pattern(chunk_pattern, &dwu);
+ fprintf(stderr, "An inconsistent chunk record found!\n"
+ "Expected:\tchunkno(%ld)\ttimestamp(%llu)\tchar(%c)\n"
+ "Found :\tchunkno(%ld)\ttimestamp(%llu)\tchar(%c)\n",
+ dwus[i].d_chunk_no, dwus[i].d_timestamp, dwus[i].d_char,
+ dwu.d_chunk_no, dwu.d_timestamp, dwu.d_char);
+ ret = -1;
+ goto bail;
+
+ }
+
+ }
+
+bail:
+ if (dwus)
+ free(dwus);
+
+ if (logfile)
+ fclose(logfile);
+
+ if (fd)
+ close(fd);
+
+ return ret;
+}
--
1.5.5
More information about the Ocfs2-test-devel
mailing list