4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 only,
8 * as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License version 2 for more details (a copy is included
14 * in the LICENSE file that accompanied this code).
16 * You should have received a copy of the GNU General Public License
17 * version 2 along with this program; If not, see
18 * http://www.gnu.org/licenses/gpl-2.0.html
23 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Use is subject to license terms.
26 * Copyright (c) 2012, 2017, Intel Corporation.
29 * This file is part of Lustre, http://www.lustre.org/
33 #define _GNU_SOURCE /* pull in O_DIRECTORY in bits/fcntl.h */
41 #include <sys/types.h>
45 #include <sys/ioctl.h>
46 #include <sys/xattr.h>
51 #include <semaphore.h>
57 #include <lustre/lustreapi.h>
59 #define T1 "write data before unlink\n"
60 #define T2 "write data after unlink\n"
61 char msg[] = "yabba dabba doo, I'm coming for you, I live in a shoe, I don't know what to do.\n'Bigger, bigger,and bigger yet!' cried the Creator. 'You are not yet substantial enough for my boundless intents!' And ever greater and greater the object became, until all was lost 'neath its momentus bulk.\n";
62 char *buf, *buf_align;
65 #define ALIGN_LEN 65535
66 #define XATTR "user.multiop"
69 "Usage: %s filename command-sequence [path...]\n"
70 " command-sequence items:\n"
71 " A fsetxattr(\"user.multiop\")\n"
72 " a[num] fgetxattr(\"user.multiop\") [optional buffer size, default 0]\n"
74 " B[num] call setstripe ioctl to create stripes\n"
75 " C[num] create with optional stripes\n"
77 " D open(O_DIRECTORY)\n"
78 " e[R|W|U] apply lease. R: Read; W: Write; U: Unlock\n"
79 " E[+|-] get lease. +/-: expect lease to (not) exist\n"
82 " G gid get grouplock\n"
83 " g gid put grouplock\n"
84 " H[num] create HSM released file with num stripes\n"
85 " K link path to filename\n"
87 " l symlink filename to path\n"
89 " M rw mmap to EOF (must open and stat prior)\n"
90 " n rename path to filename\n"
91 " N rename filename to path\n"
93 " O open(O_CREAT|O_RDWR)\n"
94 " p print return value of last command\n"
95 " Q open filename (should be dir), stat first entry to init statahead"
96 " r[num] read [optional length]\n"
97 " R reference entire mmap-ed region\n"
101 " T[num] ftruncate [optional position, default 0]\n"
105 " V open a volatile file\n"
106 " w[num] write optional length\n"
107 " P[num] like w, but only one write call\n"
108 " x get file data version\n"
109 " W write entire mmap-ed region\n"
112 " z[num] lseek(SEEK_SET) [optional offset, default 0]\n"
113 " Z[num] lseek(SEEK_CUR) [optional offset, default 0]\n"
114 " _ wait for signal\n";
116 static void usr1_handler(int unused)
118 int saved_errno = errno;
121 * signal(7): POSIX.1-2004 ...requires an implementation to guarantee
122 * that the following functions can be safely called inside a signal
132 pop_arg(int argc, char *argv[])
134 static int cur_arg = 3;
139 return argv[cur_arg++];
142 struct flag_mapping {
146 {"O_RDONLY", O_RDONLY},
147 {"O_WRONLY", O_WRONLY},
149 {"O_CREAT", O_CREAT},
151 {"O_NOCTTY", O_NOCTTY},
152 {"O_TRUNC", O_TRUNC},
153 {"O_APPEND", O_APPEND},
154 {"O_NONBLOCK", O_NONBLOCK},
155 {"O_NDELAY", O_NDELAY},
158 {"O_DIRECT", O_DIRECT},
161 {"O_NOATIME", O_NOATIME},
163 {"O_LARGEFILE", O_LARGEFILE},
164 {"O_DIRECTORY", O_DIRECTORY},
165 {"O_NOFOLLOW", O_NOFOLLOW},
166 {"O_LOV_DELAY_CREATE", O_LOV_DELAY_CREATE},
170 static int get_flags(char *data, int *rflags)
178 cloned_flags = strdup(data);
180 fprintf(stderr, "Insufficient memory.\n");
184 for (tmp = strtok(cloned_flags, ":"); tmp;
185 tmp = strtok(NULL, ":")) {
188 size = tmp - cloned_flags;
189 for (i = 0; flag_table[i].flag != -1; i++) {
190 if (!strcmp(tmp, flag_table[i].string)) {
191 flags |= flag_table[i].flag;
192 size += strlen(flag_table[i].string);
209 static int statahead(char *dname)
226 if (asprintf(&buf, "%s/%s", dname, dent->d_name) == -1) {
238 #define POP_ARG() (pop_arg(argc, argv))
240 int main(int argc, char **argv)
242 char *fname, *commands;
247 size_t mmap_len = 0, i;
248 unsigned char *mmap_ptr = NULL, junk = 1;
256 struct lov_user_md_v3 lum;
257 char *xattr_buf = NULL;
258 size_t xattr_buf_size = 0;
262 int msg_len = strlen(msg);
266 fprintf(stderr, usage, argv[0]);
270 memset(&st, 0, sizeof(st));
271 sem_init(&sem, 0, 0);
272 /* use sigaction instead of signal to avoid SA_ONESHOT semantics */
274 &(const struct sigaction){.sa_handler = &usr1_handler}, NULL);
278 for (commands = argv[2]; *commands; commands++) {
280 * XXX Most commands return 0 or we exit so we only
281 * update rc where really needed.
294 len = atoi(commands + 1);
296 len = 3600; /* 1 hour */
297 ts.tv_sec = time(NULL) + len;
299 while (sem_timedwait(&sem, &ts) < 0 && errno == EINTR)
303 if (fsetxattr(fd, XATTR, "multiop", 8, 0)) {
310 len = atoi(commands + 1);
311 if (xattr_buf_size < len) {
312 xattr_buf = realloc(xattr_buf, len);
315 perror("allocating xattr buffer\n");
319 xattr_buf_size = len;
322 rc = fgetxattr(fd, XATTR, xattr_buf, len);
330 if (close(fd) == -1) {
338 lum = (struct lov_user_md_v3) {
339 .lmm_magic = LOV_USER_MAGIC_V3,
340 .lmm_stripe_count = atoi(commands + 1),
343 if (ioctl(fd, LL_IOC_LOV_SETSTRIPE, &lum) < 0) {
345 perror("LL_IOC_LOV_SETSTRIPE");
350 len = atoi(commands + 1);
351 fd = llapi_file_open(fname, O_CREAT | O_WRONLY, 0644,
355 perror("create stripe file");
361 if (mkdir(fname, 0755) == -1) {
363 perror("mkdir(0755)");
368 fd = open(fname, O_DIRECTORY);
371 perror("open(O_DIRECTORY)");
380 rc = llapi_lease_release(fd);
383 rc = llapi_lease_acquire(fd, LL_LEASE_RDLCK);
386 rc = llapi_lease_acquire(fd, LL_LEASE_WRLCK);
389 errx(-1, "unknown mode: %c", *commands);
392 err(errno, "apply/unlock lease error");
394 if (flags != LL_LEASE_UNLCK)
397 /* F_UNLCK, interpret return code */
399 const char *str = "unknown";
401 if (rc == LL_LEASE_RDLCK)
403 else if (rc == LL_LEASE_WRLCK)
405 fprintf(stdout, "%s lease(%lld) released.\n",
407 } else if (rc == 0) {
408 fprintf(stdout, "lease already broken.\n");
413 if (*commands != '-' && *commands != '+')
414 errx(-1, "unknown mode: %c\n", *commands);
416 rc = llapi_lease_check(fd);
418 const char *str = "unknown";
420 if (rc == LL_LEASE_RDLCK)
422 else if (rc == LL_LEASE_WRLCK)
424 fprintf(stdout, "%s lease(%lld) has applied.\n",
426 if (*commands == '-')
427 errx(-1, "expect lease to not exist");
428 } else if (rc == 0) {
429 fprintf(stdout, "no lease applied.\n");
430 if (*commands == '+')
431 errx(-1, "expect lease exists");
433 err(errno, "free lease error");
437 if (statfs(fname, &stfs) == -1)
438 errx(-1, "statfs()");
442 rc = llapi_path2fid(fname, &fid);
444 rc = llapi_fd2fid(fd, &fid);
447 "llapi_path/fd2fid() on %d, rc=%lld\n",
450 printf(DFID"\n", PFID(&fid));
454 gid = atoi(commands + 1);
455 if (ioctl(fd, LL_IOC_GROUP_LOCK, gid) == -1) {
457 perror("ioctl(GROUP_LOCK)");
462 gid = atoi(commands + 1);
463 if (ioctl(fd, LL_IOC_GROUP_UNLOCK, gid) == -1) {
465 perror("ioctl(GROUP_UNLOCK)");
470 len = atoi(commands + 1);
471 fd = llapi_file_open(fname, O_CREAT | O_WRONLY, 0644,
472 0, 0, len, LOV_PATTERN_RAID0 |
473 LOV_PATTERN_F_RELEASED);
476 perror("create stripe file");
482 if (flock(fd, LOCK_EX) == -1)
490 if (link(oldpath, fname)) {
500 if (symlink(fname, newfile)) {
511 if (link(fname, newfile)) {
518 if (mknod(fname, S_IFREG | 0644, 0) == -1) {
520 perror("mknod(S_IFREG|0644, 0)");
525 if (st.st_size == 0) {
527 "mmap without preceeding stat, or on zero length file.\n");
530 mmap_len = st.st_size;
531 mmap_ptr = mmap(NULL, mmap_len, PROT_WRITE | PROT_READ,
533 if (mmap_ptr == MAP_FAILED) {
544 if (rename(oldpath, fname) < 0) {
554 if (rename(fname, newfile)) {
561 fd = open(fname, O_CREAT | O_RDWR, 0644);
564 perror("open(O_RDWR|O_CREAT)");
570 len = get_flags(commands + 1, &flags);
573 fd = open(fname, flags, 0666);
575 fd = open(fname, flags);
584 printf("%lld\n", last_rc);
587 save_errno = statahead(fname);
594 if (*(commands + 1) == 'u') {
598 len = atoi(commands + 1);
601 /* for unaligned, we realloc every time, so the
602 * buffer alignment is variable
604 * the last condition is "if buf is unaligned", so when
605 * unaligned is not set, we realloc if the buf is
606 * unaligned to create an aligned buffer
608 if (bufsize < len || unaligned ||
610 (char *)((long)(buf + ALIGN_LEN) & ~ALIGN_LEN)) {
613 /* We add a margin of + ALIGN_LEN to let us
614 * unalign and stay in the buffer
616 tmp = realloc(buf, len + ALIGN_LEN*2);
620 perror("allocating buf for write\n");
625 buf_align = (char *)((long)(buf + ALIGN_LEN) &
627 /* if the original buffer was aligned, we
628 * manually unalign it. Otherwise, we use
629 * the unalignment from the allocator.
631 * Add + 1 to avoid ever hitting 0, and use
632 * mod 255 to avoid 255 + 1 = 256
634 if (unaligned && buf_align == buf)
635 buf_align += rand() % 255 + 1;
643 start = lseek(fd, 0, SEEK_CUR);
644 rc = read(fd, buf_align, len);
651 off = lseek(fd, 0, SEEK_CUR);
652 fprintf(stderr, "short read: %ld ->+ %u -> %ld %lld\n",
653 start, len, off, rc);
659 printf("Buffer address %s: %p\n",
660 unaligned ? "(unaligned)" : "",
662 printf("Read this (%lld bytes):\n", rc);
663 printf("%.*s\n", (int)rc, buf_align);
668 for (i = 0; i < mmap_len && mmap_ptr; i += 4096)
672 if (stat(fname, &st) == -1) {
679 if (fstat(fd, &st) == -1) {
686 if (fchmod(fd, 0) == -1) {
693 len = atoi(commands + 1);
694 if (ftruncate(fd, len) == -1) {
696 printf("ftruncate (%d,%d)\n", fd, len);
702 if (unlink(fname) == -1) {
709 if (munmap(mmap_ptr, mmap_len)) {
719 len = get_flags(commands + 1, &flags);
721 len = -1; /* mdt index */
722 if (commands[1] >= '0' && commands[1] <= '9')
723 len = atoi(commands + 1);
724 fd = llapi_create_volatile_idx(fname, len, flags);
726 perror("llapi_create_volatile");
733 if (*(commands + 1) == 'u') {
737 len = atoi(commands + 1);
740 /* for unaligned, we realloc every time, so the
741 * buffer alignment is variable
743 * the last condition is "if buf is unaligned", so when
744 * unaligned is not set, we realloc if the buf is
745 * unaligned to create an aligned buffer
747 if (bufsize < len || unaligned ||
749 (char *)((long)(buf + ALIGN_LEN) & ~ALIGN_LEN)) {
752 /* We add a margin of + ALIGN_LEN to let us
753 * unalign and stay in the buffer
755 tmp = realloc(buf, len + ALIGN_LEN*2);
759 perror("allocating buf for write\n");
764 buf_align = (char *)((long)(buf + ALIGN_LEN) &
766 /* if the original buffer was aligned, we
767 * manually unalign it. Otherwise, we use
768 * the unalignment from the allocator.
770 * Add + 1 to avoid ever hitting 0, and use
771 * mod 255 to avoid 255 + 1 = 256
773 if (unaligned && buf_align == buf)
774 buf_align += rand() % 255 + 1;
778 /* fill the buffer with our string */
779 while (total_bytes < bufsize) {
780 /* msg_len does not include the
781 * terminating nul, deliberately,
782 * so all the additions are one string
784 strncpy(buf_align + total_bytes, msg,
785 bufsize - total_bytes);
786 total_bytes += msg_len;
790 rc = write(fd, buf_align, len);
798 "short write: %lld/%u\n",
800 if (commands[0] == 'P')
804 printf("Buffer address %s: %p\n",
805 unaligned ? "(unaligned)" : "",
807 printf("Wrote this (%lld bytes):\n",
809 printf("%.*s\n", (int)rc, buf_align);
814 for (i = 0; i < mmap_len && mmap_ptr; i += 4096)
815 mmap_ptr[i] += junk++;
820 rc = llapi_get_data_version(fd, &dv, 0);
823 "cannot get file data version %lld\n",
827 printf("dataversion is %ju\n", (uintmax_t)dv);
831 __u32 layout_version;
833 rc = llapi_get_ost_layout_version(fd, &layout_version);
836 "cannot get ost layout version %lld\n",
840 printf("ostlayoutversion: %u\n", layout_version);
844 if (fsync(fd) == -1) {
851 if (fdatasync(fd) == -1) {
860 len = atoi(commands + 1);
861 off = lseek(fd, len, SEEK_SET);
862 if (off == (off_t)-1) {
874 len = atoi(commands + 1);
875 off = lseek(fd, len, SEEK_CUR);
876 if (off == (off_t)-1) {
898 fprintf(stderr, "unknown command \"%c\"\n", *commands);
899 fprintf(stderr, usage, argv[0]);