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,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License version 2 for more details. A copy is
14 * included in the COPYING file that accompanied this code.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 * Copyright (c) 2017, Intel Corporation.
25 * lustre/utils/lsnapshot.c
27 * Author: Fan, Yong <fan.yong@intel.com>
37 #include <sys/types.h>
45 #include <libcfs/util/list.h>
46 #include <libcfs/util/ioctl.h>
47 #include <libcfs/util/string.h>
48 #include <linux/lustre/lustre_ioctl.h>
49 #include <linux/lustre/lustre_barrier_user.h>
53 #define SNAPSHOT_CONF_DIR "/etc/lsnapshot"
54 #define LDEV_CONF "/etc/ldev.conf"
55 #define SNAPSHOT_LOG "/var/log/lsnapshot.log"
56 #define SNAPSHOT_MAGIC "0x14F711B9"
57 #define MAX_BUF_SIZE 4096
65 struct snapshot_target {
66 struct list_head st_list;
67 /* Target node name. */
70 /* Where the pool is */
72 /* The target pool name on the target node. */
74 /* The backend filesystem name against the target pool. */
77 unsigned int st_index;
85 struct snapshot_instance {
86 struct list_head si_mdts_list;
87 struct list_head si_osts_list;
88 struct snapshot_target *si_mgs;
89 struct snapshot_target *si_mdt0;
103 static const char snapshot_rsh_default[] = "ssh";
104 static char snapshot_path[MAX_BUF_SIZE];
106 static char *snapshot_role2name(char *name, enum snapshot_role role,
110 snprintf(name, 8, "MDT%04x", index);
111 else if (role & SR_MGS)
112 snprintf(name, 4, "MGS");
114 snprintf(name, 8, "OST%04x", index);
119 #define SNAPSHOT_ADD_LOG(si, format, ...) \
121 char buf[MAX_BUF_SIZE]; \
125 memset(buf, 0, sizeof(buf)); \
127 snprintf(buf, sizeof(buf) - 1, "%s", ctime(&tt)); \
128 ptr = strrchr(buf, '\n'); \
132 fprintf(si->si_log_fp, "%s (%d:%s:%d:%s:%s): "format, buf, \
133 getpid(), __func__, __LINE__, si->si_fsname, \
134 si->si_rsh, ## __VA_ARGS__); \
138 #define DFSNAME "%s/%s"
139 #define DSSNAME "%s/%s@%s"
140 #define DZFS "%s zfs"
141 #define DIMPORT "%s zpool import -d %s %s > /dev/null 2>&1"
143 #define PRSH(si, st, foreign) (si)->si_rsh, \
144 foreign ? (st)->st_fhost : (st)->st_host
145 #define PFSNAME(st) (st)->st_pool, (st)->st_filesystem
146 #define PSSNAME(si, st) PFSNAME(st), (si)->si_ssname
147 #define PSS_NEW(si, st) PFSNAME(st), (si)->si_new_ssname
148 #define PZFS(st) snapshot_path
149 #define PIMPORT(st) snapshot_path, \
150 (st)->st_dir ? (st)->st_dir : "/dev -d /tmp", (st)->st_pool
152 static char *snapshot_fgets(FILE *fp, char *buf, int buflen)
156 memset(buf, 0, buflen);
157 if (fgets(buf, buflen, fp) == NULL)
160 ptr = strchr(buf, '\n');
167 static int snapshot_exec(const char *cmd)
173 /* system() return value depends on both the system() general framework,
174 * such as whether fork()/exec() success or fail, and the real @cmd exec
175 * result. Especially, if the @cmd is remote command, we may cannot know
176 * the real failure. */
178 /* fork()/exec() error */
180 return errno != 0 ? -errno : -1;
183 rc = WEXITSTATUS(rc);
186 } else if (WIFSIGNALED(rc)) {
189 /* all other known or unknown cases. */
196 static int snapshot_load_conf_ldev(struct snapshot_instance *si, char *buf,
197 struct snapshot_target *st, char **role)
206 rc = sscanf(buf, "%ms %ms %ms %ms",
207 &st->st_host, &st->st_fhost, &label, &device);
212 if (strcmp(st->st_fhost, "-") == 0)
216 * [md|zfs:][pool_dir/]<pool>/<filesystem> */
217 ptr = strchr(device, ':');
220 if (strncmp(device, "zfs:", strlen("zfs:")) != 0) {
228 ptr1 = strrchr(ptr, '/');
229 /* "ptr1 - ptr + 1 == strlen(ptr)" means '/' is at the tail. */
230 if (!ptr1 || ptr1 == ptr || ptr1 - ptr + 1 == strlen(ptr)) {
236 st->st_filesystem = malloc(len);
237 if (!st->st_filesystem) {
243 strncpy(st->st_filesystem, ptr1 + 1, len - 1);
244 st->st_filesystem[len - 1] = '\0';
247 ptr1 = strrchr(ptr, '/');
249 st->st_dir = strdup(ptr);
257 st->st_pool = strdup(ptr);
264 * fsname-<role><index> or <role><index> */
265 ptr = strrchr(label, '-');
267 if (strncmp(si->si_fsname, label, ptr - label) != 0) {
268 /* This line is NOT for current filesystem .*/
278 if (strlen(ptr) < 3 || strlen(ptr) > 7) {
289 strncpy(*role, ptr, 3);
293 while (isxdigit(ptr[len])) {
294 if (isdigit(ptr[len]))
296 st->st_index * 16 + ptr[len] - '0';
297 else if (isupper(ptr[len]))
299 st->st_index * 16 + ptr[len] - 'A' + 10;
302 st->st_index * 16 + ptr[len] - 'a' + 10;
307 if (strncasecmp(*role, "MGS", 3) != 0)
315 if (!isxdigit(ptr[len]) && ptr[len] != '\0') {
330 * For old snasphot tools, the configration is in /etc/lsnapshot/${fsname}.conf,
332 * <host> <pool_dir> <pool> <local_fsname> <role(,s)> <index>
336 * host-mdt1 /tmp myfs-mdt1 mdt1 MGS,MDT 0
337 * host-mdt2 /tmp myfs-mdt2 mdt2 MDT 1
338 * host-ost1 /tmp myfs-ost1 ost1 OST 0
339 * host-ost2 /tmp myfs-ost2 ost2 OST 1
342 * For new snasphot tools, the configration is in /etc/ldev.conf, which is not
343 * only for snapshot, but also for other purpose. The format is:
344 * <host> foreign/- <label> <device> [journal-path]/- [raidtab]
346 * The format of <label> is:
347 * fsname-<role><index> or <role><index>
349 * The format of <device> is:
350 * [md|zfs:][pool_dir/]<pool>/<filesystem>
352 * Snapshot only uses the fields <host>, <label> and <device>.
356 * host-mdt1 - myfs-MDT0000 zfs:/tmp/myfs-mdt1/mdt1
359 * \retval 0 for success
360 * \retval +ve the line# with which the current line is conflict
361 * \retval -EAGAIN skip current line
362 * \retval -ve other failures
364 static int snapshot_load_conf_one(struct snapshot_instance *si,
365 char *buf, int line_num, bool is_ldev)
367 struct snapshot_target *st;
372 path = getenv("PATH");
376 memset(snapshot_path, 0, sizeof(snapshot_path));
377 snprintf(snapshot_path, sizeof(snapshot_path) - 1, "PATH='%s'", path);
379 /* filter out space */
380 while (isspace(*buf))
383 /* skip empty line */
387 /* skip comment line */
391 st = malloc(sizeof(*st));
395 memset(st, 0, sizeof(*st));
396 INIT_LIST_HEAD(&st->st_list);
399 rc = snapshot_load_conf_ldev(si, buf, st, &role);
401 rc = sscanf(buf, "%ms %ms %ms %ms %ms %ms %d",
402 &st->st_host, &st->st_fhost, &st->st_dir,
403 &st->st_pool, &st->st_filesystem, &role,
408 if (strcmp(st->st_fhost, "-") == 0)
416 if (strncasecmp(role, "MGS", 3) == 0) {
417 st->st_role = SR_MGS;
418 if (role[3] == ',') {
420 if (strncasecmp(&role[4], "MDT", 3) != 0) {
425 st->st_role |= SR_MDT;
427 } else if (strncasecmp(role, "MDT", 3) == 0) {
428 st->st_role = SR_MDT;
429 if (role[3] == ',') {
431 if (strncasecmp(&role[4], "MGS", 3) != 0) {
436 st->st_role |= SR_MGS;
438 } else if (strncasecmp(role, "OST", 3) == 0) {
439 st->st_role = SR_OST;
445 st->st_line = line_num;
446 if (st->st_role & SR_MDT) {
447 /* MGS is the first, MDT0 is just after the MGS
448 * if they are not combined together. */
449 if (st->st_role & SR_MGS) {
451 rc = si->si_mgs->st_line;
456 list_add(&st->st_list, &si->si_mdts_list);
459 if (st->st_index == 0) {
461 rc = si->si_mdt0->st_line;
466 if (list_empty(&st->st_list)) {
467 if (list_empty(&si->si_mdts_list) ||
469 list_add(&st->st_list,
472 list_add(&st->st_list,
473 &si->si_mgs->st_list);
475 } else if (list_empty(&st->st_list)) {
476 list_add_tail(&st->st_list, &si->si_mdts_list);
478 } else if (st->st_role & SR_MGS) {
480 rc = si->si_mgs->st_line;
485 list_add(&st->st_list, &si->si_mdts_list);
487 list_add_tail(&st->st_list, &si->si_osts_list);
503 if (st->st_filesystem)
504 free(st->st_filesystem);
511 static int snapshot_load_conf(struct snapshot_instance *si, int lock_mode)
514 char buf[MAX_BUF_SIZE];
521 memset(conf_name, 0, sizeof(conf_name));
522 strncpy(conf_name, LDEV_CONF, sizeof(conf_name) - 1);
523 fd = open(conf_name, O_RDONLY);
525 if (errno != ENOENT) {
527 "Can't open the snapshot config file %s: %s\n",
528 conf_name, strerror(errno));
533 snprintf(conf_name, sizeof(conf_name) - 1, "%s/%s.conf",
534 SNAPSHOT_CONF_DIR, si->si_fsname);
535 fd = open(conf_name, O_RDONLY);
538 "Can't open the snapshot config file %s: %s\n",
539 conf_name, strerror(errno));
547 rc = flock(fd, lock_mode | LOCK_NB);
550 "Can't lock the snapshot config file %s (%d): %s\n",
551 conf_name, lock_mode, strerror(errno));
556 fp = fdopen(fd, "r");
559 "Can't fdopen the snapshot config file %s: %s\n",
560 conf_name, strerror(errno));
565 while (snapshot_fgets(fp, buf, MAX_BUF_SIZE) != NULL) {
566 rc = snapshot_load_conf_one(si, buf, line_num, is_ldev);
569 "Invalid snapshot config file %s at the line "
570 "%d '%s'\n", conf_name, line_num, buf);
571 } else if (rc == -EAGAIN) {
575 "The config role has been specified repeatedly "
576 "at the lines %d/%d in %s\n",
577 rc, line_num, conf_name);
589 "Miss MDT0 in the config file %s\n",
595 /* By default, the MGS is on the MDT0 if it is not specified. */
597 si->si_mgs = si->si_mdt0;
598 si->si_mgs->st_role |= SR_MGS;
601 if (list_empty(&si->si_osts_list)) {
603 "Miss OST(s) in the config file %s\n",
616 /* fclose() closes any associated underlying file descriptor.
617 * explicit close(fd) not required
626 static void snapshot_unload_conf(struct snapshot_instance *si)
628 struct snapshot_target *st;
630 while (!list_empty(&si->si_mdts_list)) {
631 st = list_first_entry(&si->si_mdts_list,
632 struct snapshot_target, st_list);
633 list_del(&st->st_list);
638 free(st->st_filesystem);
642 while (!list_empty(&si->si_osts_list)) {
643 st = list_first_entry(&si->si_osts_list,
644 struct snapshot_target, st_list);
645 list_del(&st->st_list);
650 free(st->st_filesystem);
657 if (si->si_conf_fd >= 0) {
658 flock(si->si_conf_fd, LOCK_UN);
659 close(si->si_conf_fd);
664 static int snapshot_handle_string_option(char **dst, const char *option,
665 const char *opt_name)
667 if (*dst && *dst != snapshot_rsh_default) {
669 "%s option has been specified repeatedly.\n", opt_name);
673 *dst = strdup(option);
679 static void snapshot_fini(struct snapshot_instance *si)
681 snapshot_unload_conf(si);
684 fclose(si->si_log_fp);
686 if (si->si_rsh && si->si_rsh != snapshot_rsh_default)
692 if (si->si_new_ssname)
693 free(si->si_new_ssname);
695 free(si->si_comment);
700 static struct snapshot_instance *
701 snapshot_init(int argc, char * const argv[], const struct option *longopts,
702 const char *optstring, void (*usage)(void),
703 int lock_mode, int *err)
705 struct snapshot_instance *si;
710 si = malloc(sizeof(*si));
713 "No enough memory to initialize snapshot instance.\n");
718 memset(si, 0, sizeof(*si));
719 INIT_LIST_HEAD(&si->si_mdts_list);
720 INIT_LIST_HEAD(&si->si_osts_list);
721 si->si_rsh = (char *)snapshot_rsh_default;
723 si->si_timeout = BARRIER_TIMEOUT_DEFAULT;
724 si->si_barrier = true;
725 si->si_detail = false;
726 si->si_force = false;
728 while ((opt = getopt_long(argc, argv, optstring,
729 longopts, &idx)) != EOF) {
732 if (!optarg || strcmp(optarg, "on") == 0) {
733 si->si_barrier = true;
734 } else if (strcmp(optarg, "off") == 0) {
735 si->si_barrier = false;
743 *err = snapshot_handle_string_option(&si->si_comment,
749 si->si_detail = true;
755 *err = snapshot_handle_string_option(&si->si_fsname,
761 *err = snapshot_handle_string_option(&si->si_ssname,
767 *err = snapshot_handle_string_option(&si->si_new_ssname,
774 *err = snapshot_handle_string_option(&si->si_rsh,
781 si->si_timeout = atoi(optarg);
795 if (!si->si_fsname) {
796 fprintf(stderr, "The fsname must be specified\n");
802 if (strlen(si->si_fsname) > 8) {
803 fprintf(stderr, "Invalid fsname %s\n", si->si_fsname);
808 si->si_log_fp = fopen(SNAPSHOT_LOG, "a");
809 if (!si->si_log_fp) {
812 "Can't open the snapshot log file %s: %s\n",
813 SNAPSHOT_LOG, strerror(errno));
817 *err = snapshot_load_conf(si, lock_mode);
820 if (*err != 0 && si) {
828 static int __snapshot_wait(struct snapshot_instance *si,
829 struct list_head *head, int *err)
831 struct snapshot_target *st;
835 list_for_each_entry(st, head, st_list) {
839 rc = waitpid(st->st_pid, &st->st_status, 0);
841 SNAPSHOT_ADD_LOG(si, "Can't wait child (%d) operation "
842 "on the target <%s(%s):%x:%d>: %s\n",
843 st->st_pid, st->st_host,
844 st->st_fhost ? st->st_fhost : "none",
845 st->st_role, st->st_index,
852 /* continue to wait for next */
856 if (WIFEXITED(st->st_status)) {
857 rc = WEXITSTATUS(st->st_status);
862 st->st_ignored = true;
868 } else if (WIFSIGNALED(st->st_status)) {
869 SNAPSHOT_ADD_LOG(si, "The child (%d) operation on the "
870 "target <%s(%s):%x:%d> was killed by "
872 st->st_pid, st->st_host,
873 st->st_fhost ? st->st_fhost : "none",
874 st->st_role, st->st_index,
875 WTERMSIG(st->st_status));
880 SNAPSHOT_ADD_LOG(si, "The child (%d) operation on the "
881 "target <%s(%s):%x:%d> failed for "
883 st->st_pid, st->st_host,
884 st->st_fhost ? st->st_fhost : "none",
885 st->st_role, st->st_index);
897 static int snapshot_wait(struct snapshot_instance *si, int *err)
901 count = __snapshot_wait(si, &si->si_mdts_list, err);
902 count += __snapshot_wait(si, &si->si_osts_list, err);
907 static char *snapshot_first_skip_blank(char *buf)
911 ptr = strchr(buf, ' ');
913 ptr = strchr(buf, '\t');
918 while (*ptr == ' ' || *ptr == '\t')
927 static int mdt0_is_lustre_snapshot(struct snapshot_instance *si)
929 struct snapshot_target *st = si->si_mdt0;
930 char buf[MAX_BUF_SIZE * 3];
935 memset(buf, 0, sizeof(buf));
936 snprintf(buf, sizeof(buf) - 1,
937 DRSH" '"DIMPORT"; "DZFS
938 " get -H -o value lustre:magic "DSSNAME"' 2>/dev/null",
939 PRSH(si, st, foreign), PIMPORT(st), PZFS(st), PSSNAME(si, st));
941 fp = popen(buf, "r");
943 SNAPSHOT_ADD_LOG(si, "Popen fail to check snapshot "
944 "on mdt0: %s\n", strerror(errno));
948 if (snapshot_fgets(fp, buf, strlen(SNAPSHOT_MAGIC) + 1) == NULL) {
949 if (foreign || !st->st_fhost) {
956 } else if (strcmp(buf, SNAPSHOT_MAGIC) == 0) {
960 "The target %s is not Lustre snapshot "
961 "or it does not exists\n", si->si_ssname);
969 static int target_is_mounted(struct snapshot_instance *si,
970 struct snapshot_target *st, const char *ssname)
972 char buf[MAX_BUF_SIZE];
973 char fullname[MAX_BUF_SIZE];
980 memset(buf, 0, sizeof(buf));
981 snprintf(buf, sizeof(buf) - 1,
982 DRSH" 'mount' 2>/dev/null",
983 PRSH(si, st, foreign));
984 fp = popen(buf, "r");
986 SNAPSHOT_ADD_LOG(si, "Popen fail to check target mount: %s\n",
991 memset(fullname, 0, sizeof(fullname));
993 snprintf(fullname, sizeof(fullname) - 1,
995 PFSNAME(st), ssname);
997 snprintf(fullname, sizeof(fullname) - 1,
1001 while (snapshot_fgets(fp, buf, sizeof(buf)) != NULL) {
1002 ptr = strstr(buf, fullname);
1006 ptr += strlen(fullname) + 1; /* mount point */
1007 if (ptr >= buf + strlen(buf))
1010 ptr = strstr(ptr, "type lustre");
1018 if (!rc && !foreign && st->st_fhost) {
1025 static int snapshot_get_fsname(struct snapshot_instance *si,
1026 char *fsname, int fslen)
1028 struct snapshot_target *st = si->si_mdt0;
1029 char buf[MAX_BUF_SIZE * 3];
1035 memset(buf, 0, sizeof(buf));
1036 snprintf(buf, sizeof(buf) - 1,
1037 DRSH" '"DIMPORT"; "DZFS
1038 " get -H -o value lustre:fsname "DSSNAME"' 2>/dev/null",
1039 PRSH(si, st, foreign), PIMPORT(st), PZFS(st),
1041 fp = popen(buf, "r");
1043 SNAPSHOT_ADD_LOG(si, "Popen fail to get fsname: %s\n",
1048 if (snapshot_fgets(fp, fsname, fslen) == NULL)
1053 if (rc && !foreign && st->st_fhost) {
1060 static int snapshot_get_mgsnode(struct snapshot_instance *si,
1061 char *node, int size)
1063 char buf[MAX_BUF_SIZE * 2];
1064 struct snapshot_target *st;
1069 st = list_first_entry(&si->si_osts_list, struct snapshot_target,
1073 memset(buf, 0, sizeof(buf));
1074 snprintf(buf, sizeof(buf) - 1,
1075 DRSH" '"DZFS" get -H -o value lustre:mgsnode "DFSNAME"'"
1076 " 2>/dev/null", PRSH(si, st, foreign), PZFS(st),
1078 fp = popen(buf, "r");
1080 SNAPSHOT_ADD_LOG(si, "Popen fail to get mgsnode: %s\n",
1085 if (snapshot_fgets(fp, node, size) == NULL)
1090 if (rc && !foreign && st->st_fhost) {
1098 static int snapshot_exists_check(struct snapshot_instance *si,
1099 struct snapshot_target *st)
1101 char buf[MAX_BUF_SIZE * 2];
1106 memset(buf, 0, sizeof(buf));
1107 snprintf(buf, sizeof(buf) - 1,
1108 DRSH" '"DZFS" list "DSSNAME"' 2>/dev/null",
1109 PRSH(si, st, foreign), PZFS(st), PSSNAME(si, st));
1110 fp = popen(buf, "r");
1112 SNAPSHOT_ADD_LOG(si, "Popen fail to create check: %s\n",
1117 if (snapshot_fgets(fp, buf, sizeof(buf)) != NULL)
1122 if (rc != -EEXIST && !foreign && st->st_fhost) {
1129 static int snapshot_general_check(struct snapshot_instance *si)
1131 return mdt0_is_lustre_snapshot(si);
1134 static void snapshot_create_usage(void)
1137 "Create snapshot for the given filesystem.\n"
1139 "snapshot_create [-b | --barrier [on | off]] "
1140 "[-c | --comment comment] "
1141 "<-F | --fsname fsname> "
1142 "[-h | --help] <-n | --name ssname> "
1143 "[-r | --rsh remote_shell]"
1144 "[-t | --timeout timeout]\n"
1146 "-b: set write barrier before creating snapshot, "
1147 "the default value is 'on'.\n"
1148 "-c: describe what the snapshot is for, and so on.\n"
1149 "-F: the filesystem name.\n"
1150 "-h: for help information.\n"
1151 "-n: the snapshot's name.\n"
1152 "-r: the remote shell used for communication with remote "
1153 "target, the default value is 'ssh'.\n"
1154 "-t: the life cycle (seconds) for write barrier, "
1155 "the default value is %d seconds.\n",
1156 BARRIER_TIMEOUT_DEFAULT);
1159 static int snapshot_create_check(struct snapshot_instance *si)
1163 rc = snapshot_exists_check(si, si->si_mdt0);
1165 fprintf(stderr, "The snapshot %s exists\n", si->si_ssname);
1170 static int snapshot_inherit_prop(struct snapshot_instance *si,
1171 struct snapshot_target *st,
1172 char *cmd, int size)
1174 char buf[MAX_BUF_SIZE * 3];
1180 memset(buf, 0, sizeof(buf));
1181 snprintf(buf, sizeof(buf) - 1,
1182 DRSH" \""DIMPORT"; "DZFS
1183 " get all "DFSNAME" | grep lustre: | grep local$ | "
1184 "awk '{ \\$1=\\\"\\\"; \\$NF=\\\"\\\"; print \\$0 }' | "
1185 "sed -e 's/^ //'\" 2>/dev/null",
1186 PRSH(si, st, foreign), PIMPORT(st), PZFS(st), PFSNAME(st));
1187 fp = popen(buf, "r");
1189 SNAPSHOT_ADD_LOG(si, "Popen fail to list one: %s\n",
1194 while (snapshot_fgets(fp, buf, MAX_BUF_SIZE) != NULL) {
1198 if (strncmp(buf, "lustre:fsname",
1199 strlen("lustre:fsname")) == 0)
1202 if (strncmp(buf, "lustre:magic",
1203 strlen("lustre:magic")) == 0)
1206 if (strncmp(buf, "lustre:ctime",
1207 strlen("lustre:ctime")) == 0)
1210 if (strncmp(buf, "lustre:mtime",
1211 strlen("lustre:mtime")) == 0)
1214 if (strncmp(buf, "lustre:comment",
1215 strlen("lustre:comment")) == 0)
1218 if (strncmp(buf, "lustre:svname",
1219 strlen("lustre:svname")) == 0)
1222 if (strncmp(buf, "lustre:mgsnode",
1223 strlen("lustre:mgsnode")) == 0)
1226 ptr = strchr(buf, ' ');
1228 ptr = strchr(buf, '\t');
1235 while (*ptr == ' ' || *ptr == '\t')
1241 end = strchr(ptr, ' ');
1243 end = strchr(buf, '\t');
1247 rc = scnprintf(cmd + len, size - len - 1,
1248 "-o %s=\"%s\" ", buf, ptr);
1258 if (!foreign && st->st_fhost) {
1265 static int __snapshot_create(struct snapshot_instance *si,
1266 struct list_head *head, const char *fsname,
1267 const char *mgsnode, __u64 xtime)
1269 struct snapshot_target *st;
1273 list_for_each_entry(st, head, st_list) {
1280 SNAPSHOT_ADD_LOG(si, "Can't fork for create snapshot "
1281 "(%s@%s <%s>) on the target "
1282 "(%s(%s):%x:%d): %s\n",
1283 fsname, si->si_ssname,
1284 si->si_comment, st->st_host,
1285 st->st_fhost ? st->st_fhost : "none",
1286 st->st_role, st->st_index,
1293 char cmd[MAX_BUF_SIZE];
1297 memset(cmd, 0, sizeof(cmd));
1298 len = scnprintf(cmd, sizeof(cmd) - 1,
1299 DRSH" '"DZFS" snapshot "
1300 "-o lustre:fsname=%s "
1301 "-o lustre:magic=%s "
1302 "-o lustre:ctime=%llu "
1303 "-o lustre:mtime=%llu ",
1304 PRSH(si, st, foreign), PZFS(st),
1305 fsname, SNAPSHOT_MAGIC,
1306 (unsigned long long)xtime,
1307 (unsigned long long)xtime);
1311 if (si->si_comment) {
1312 rc = scnprintf(cmd + len, sizeof(cmd) - len - 1,
1313 "-o lustre:comment=\"%s\" ",
1321 /* Make the inherited properties as local ones,
1322 * then even if others changed (or removed) the
1323 * property of the parent dataset, the snapshot
1324 * will not be affected. */
1325 rc = snapshot_inherit_prop(si, st, cmd + len,
1326 MAX_BUF_SIZE - len - 1);
1328 SNAPSHOT_ADD_LOG(si, "Can't filter property on "
1329 "target (%s:%x:%d): rc = %d\n",
1330 foreign ? st->st_fhost :
1331 st->st_host, st->st_role,
1337 if (st->st_role & SR_OST)
1338 rc = snprintf(cmd + len, sizeof(cmd) - len - 1,
1339 "-o lustre:svname=%s-OST%04x "
1340 "-o lustre:mgsnode=%s "DSSNAME"'"
1342 fsname, st->st_index, mgsnode,
1344 else if (!(st->st_role & SR_MGS) ||
1345 /* MGS is on MDT0 */
1346 si->si_mdt0 == si->si_mgs)
1347 rc = snprintf(cmd + len, sizeof(cmd) - len - 1,
1348 "-o lustre:svname=%s-MDT%04x "
1349 "-o lustre:mgsnode=%s "DSSNAME"'"
1351 fsname, st->st_index, mgsnode,
1355 rc = snprintf(cmd + len, sizeof(cmd) - len - 1,
1356 DSSNAME"' 2>/dev/null",
1361 rc = snapshot_exec(cmd);
1363 SNAPSHOT_ADD_LOG(si, "Can't execute \"%s\" on "
1364 "target (%s:%x:%d): rc = %d\n",
1365 cmd, foreign ? st->st_fhost :
1366 st->st_host, st->st_role,
1368 if (!foreign && st->st_fhost) {
1375 } /* end of child */
1377 /* parent continue to run more snapshot commands in parallel. */
1384 static int __snapshot_destroy(struct snapshot_instance *si,
1385 struct list_head *head);
1387 static int snapshot_create(struct snapshot_instance *si)
1390 char buf[MAX_BUF_SIZE];
1397 rc = snapshot_create_check(si);
1401 rc = gettimeofday(&tv, NULL);
1405 srandom(tv.tv_usec);
1406 snprintf(new_fsname, sizeof(new_fsname), "%08x", (__u32)random());
1408 rc = snapshot_get_mgsnode(si, buf, sizeof(buf));
1412 __argv[1] = si->si_fsname;
1413 /* 1. Get barrier */
1414 if (si->si_barrier) {
1417 memset(tbuf, 0, sizeof(tbuf));
1418 snprintf(tbuf, 7, "%u", si->si_timeout);
1419 __argv[0] = "barrier_freeze";
1421 rc = jt_barrier_freeze(3, __argv);
1423 SNAPSHOT_ADD_LOG(si, "Can't set barrier within %u "
1424 "seconds on %s: rc = %d\n",
1425 si->si_timeout, si->si_fsname, rc);
1431 /* 2. Fork config llog on MGS */
1432 __argv[0] = "fork_lcfg";
1433 __argv[2] = new_fsname;
1434 rc = jt_lcfg_fork(3, __argv);
1436 SNAPSHOT_ADD_LOG(si, "Can't fork config log for create "
1437 "snapshot %s from %s to %s: rc = %d\n",
1438 si->si_ssname, si->si_fsname, new_fsname, rc);
1442 /* 3.1 Create snapshot on every MDT */
1443 rc = __snapshot_create(si, &si->si_mdts_list, new_fsname, buf,
1446 /* 3.2 Create snapshot on every OST */
1447 rc = __snapshot_create(si, &si->si_osts_list, new_fsname, buf,
1450 /* 4. Wait for all children, even though part of them maybe failed */
1451 snapshot_wait(si, &rc1);
1454 /* 5. Put barrier */
1455 if (si->si_barrier) {
1457 struct barrier_ctl bc;
1459 rc = __jt_barrier_stat(__argv[1], &bc);
1461 SNAPSHOT_ADD_LOG(si, "Can't get barrier status "
1464 } else if (bc.bc_status != BS_FROZEN ||
1465 bc.bc_timeout <= 0) {
1466 SNAPSHOT_ADD_LOG(si, "The barrier expired "
1467 "on %s\n", si->si_fsname);
1472 __argv[0] = "barrier_thaw";
1473 rc2 = jt_barrier_thaw(2, __argv);
1475 SNAPSHOT_ADD_LOG(si, "Can't release barrier on %s: "
1476 "rc = %d\n", si->si_fsname, rc2);
1481 si->si_force = true;
1482 __snapshot_destroy(si, &si->si_osts_list);
1483 __snapshot_destroy(si, &si->si_mdts_list);
1484 snapshot_wait(si, &rc2);
1486 __argv[0] = "erase_lcfg";
1487 __argv[1] = new_fsname;
1489 jt_lcfg_erase(3, __argv);
1492 return rc ? rc : (rc1 ? rc1 : rc2);
1495 int jt_snapshot_create(int argc, char **argv)
1497 struct snapshot_instance *si;
1498 struct option long_opts[] = {
1499 { .val = 'b', .name = "barrier", .has_arg = optional_argument },
1500 { .val = 'c', .name = "comment", .has_arg = required_argument },
1501 { .val = 'F', .name = "fsname", .has_arg = required_argument },
1502 { .val = 'h', .name = "help", .has_arg = no_argument },
1503 { .val = 'n', .name = "name", .has_arg = required_argument },
1504 { .val = 'r', .name = "rsh", .has_arg = required_argument },
1505 { .val = 't', .name = "timeout", .has_arg = required_argument },
1509 si = snapshot_init(argc, argv, long_opts, "b::c:F:hn:r:t:",
1510 snapshot_create_usage, LOCK_EX, &rc);
1514 if (!si->si_ssname) {
1516 "Miss the snapshot name to be created\n");
1517 snapshot_create_usage();
1522 rc = snapshot_create(si);
1525 "Can't create the snapshot %s\n", si->si_ssname);
1526 SNAPSHOT_ADD_LOG(si, "Can't create snapshot %s with "
1527 "comment <%s> barrier <%s>, timeout "
1529 si->si_ssname, si->si_comment,
1530 si->si_barrier ? "enable" : "disable",
1531 si->si_barrier ? si->si_timeout : -1, rc);
1533 SNAPSHOT_ADD_LOG(si, "Create snapshot %s successfully "
1534 "with comment <%s>, barrier <%s>, "
1536 si->si_ssname, si->si_comment,
1537 si->si_barrier ? "enable" : "disable",
1538 si->si_barrier ? si->si_timeout : -1);
1545 static void snapshot_destroy_usage(void)
1548 "Destroy the specified snapshot.\n"
1550 "snapshot_destroy [-f | --force] "
1551 "<-F | --fsname fsname> [-h | --help] "
1552 "<-n | --name ssname> "
1553 "[-r | --rsh remote_shell]\n"
1555 "-f: destroy the snapshot by force.\n"
1556 "-F: the filesystem name.\n"
1557 "-h: for help information.\n"
1558 "-n: the snapshot's name.\n"
1559 "-r: the remote shell used for communication with remote "
1560 "target, the default value is 'ssh'.\n");
1563 static int snapshot_destroy_check(struct snapshot_instance *si)
1565 struct list_head *head = &si->si_osts_list;
1566 struct snapshot_target *st;
1571 list_for_each_entry(st, head, st_list) {
1578 SNAPSHOT_ADD_LOG(si, "Can't fork for check snapshot "
1579 "%s on the target (%s(%s):%x:%d): "
1581 si->si_ssname, st->st_host,
1582 st->st_fhost ? st->st_fhost : "none",
1583 st->st_role, st->st_index,
1590 rc = snapshot_exists_check(si, st);
1592 /* The snapshot piece does not exist */
1595 exit(rc == -EEXIST ? 0: rc);
1596 } /* end of child */
1598 /* parent continue to run more snapshot commands in parallel. */
1602 if (head == &si->si_osts_list) {
1603 head = &si->si_mdts_list;
1607 snapshot_wait(si, &rc);
1611 head = &si->si_osts_list;
1614 list_for_each_entry(st, head, st_list) {
1615 if (st->st_ignored && !si->si_force) {
1618 snapshot_role2name(name, st->st_role, st->st_index);
1620 "Miss snapshot piece on the %s. Use '-f' "
1621 "option if want to destroy it by force.\n",
1628 if (head == &si->si_osts_list) {
1629 head = &si->si_mdts_list;
1634 rc = snapshot_general_check(si);
1639 static int __snapshot_destroy(struct snapshot_instance *si,
1640 struct list_head *head)
1642 struct snapshot_target *st;
1646 list_for_each_entry(st, head, st_list) {
1655 SNAPSHOT_ADD_LOG(si, "Can't fork for destroy snapshot "
1656 "%s on the target (%s(%s):%x:%d): "
1658 si->si_ssname, st->st_host,
1659 st->st_fhost ? st->st_fhost : "none",
1660 st->st_role, st->st_index,
1667 char cmd[MAX_BUF_SIZE * 2];
1670 memset(cmd, 0, sizeof(cmd));
1672 snprintf(cmd, sizeof(cmd) - 1,
1673 DRSH" 'umount -f "DSSNAME
1674 " > /dev/null 2>&1; "DZFS
1675 " destroy -f "DSSNAME"'"
1677 PRSH(si, st, foreign),
1678 PSSNAME(si, st), PZFS(st),
1681 snprintf(cmd, sizeof(cmd) - 1,
1682 DRSH" '"DZFS" destroy "DSSNAME"'"
1684 PRSH(si, st, foreign), PZFS(st),
1686 rc = snapshot_exec(cmd);
1688 SNAPSHOT_ADD_LOG(si, "Can't execute \"%s\" on "
1689 "target (%s:%x:%d): rc = %d\n",
1690 cmd, foreign ? st->st_fhost :
1691 st->st_host, st->st_role,
1693 if (!foreign && st->st_fhost) {
1700 } /* end of child */
1702 /* parent continue to run more snapshot commands in parallel. */
1709 static int snapshot_destroy(struct snapshot_instance *si)
1717 rc = snapshot_destroy_check(si);
1721 rc = snapshot_get_fsname(si, fsname, sizeof(fsname));
1725 /* 1.1 Destroy snapshot on every OST */
1726 rc = __snapshot_destroy(si, &si->si_osts_list);
1727 if (!si->si_force) {
1731 __snapshot_wait(si, &si->si_osts_list, &rc);
1736 /* 1.2 Destroy snapshot on every MDT */
1737 rc1 = __snapshot_destroy(si, &si->si_mdts_list);
1739 /* 2 Wait for all children, even though part of them maybe failed */
1740 snapshot_wait(si, &rc2);
1741 if (rc2 == -ENOENT && si->si_force)
1744 /* 3. Erase config llog from MGS */
1745 if ((!rc && !rc1 && !rc2) || si->si_force) {
1748 __argv[0] = "erase_lcfg";
1751 rc3 = jt_lcfg_erase(3, __argv);
1752 if (rc3 && errno == ENOENT)
1755 SNAPSHOT_ADD_LOG(si, "Can't erase config for destroy "
1756 "snapshot %s, fsname %s: rc = %d\n",
1757 si->si_ssname, fsname, rc3);
1760 return rc ? rc : (rc1 ? rc1 : (rc2 ? rc2 : rc3));
1763 int jt_snapshot_destroy(int argc, char **argv)
1765 struct snapshot_instance *si;
1766 struct option long_opts[] = {
1767 { .val = 'f', .name = "force", .has_arg = no_argument },
1768 { .val = 'F', .name = "fsname", .has_arg = required_argument },
1769 { .val = 'h', .name = "help", .has_arg = no_argument },
1770 { .val = 'n', .name = "name", .has_arg = required_argument },
1771 { .val = 'r', .name = "rsh", .has_arg = required_argument },
1775 si = snapshot_init(argc, argv, long_opts, "fF:hn:r:",
1776 snapshot_destroy_usage, LOCK_EX, &rc);
1780 if (!si->si_ssname) {
1782 "Miss the snapshot name to be destroyed\n");
1783 snapshot_destroy_usage();
1788 rc = snapshot_destroy(si);
1791 "Can't destroy the snapshot %s\n", si->si_ssname);
1792 SNAPSHOT_ADD_LOG(si, "Can't destroy snapshot %s with "
1793 "force <%s>: %d\n", si->si_ssname,
1794 si->si_force ? "enable" : "disable", rc);
1796 SNAPSHOT_ADD_LOG(si, "Destroy snapshot %s successfully "
1797 "with force <%s>\n", si->si_ssname,
1798 si->si_force ? "enable" : "disable");
1805 static void snapshot_modify_usage(void)
1808 "Change the specified snapshot's name and/or comment.\n"
1810 "snapshot_modify [-c | --comment comment] "
1811 "<-F | --fsname fsname> [-h | --help] "
1812 "<-n | --name ssname> [-N | --new new_ssname] "
1813 "[-r | --rsh remote_shell]\n"
1815 "-c: update the snapshot's comment.\n"
1816 "-F: the filesystem name.\n"
1817 "-h: for help information.\n"
1818 "-n: the snapshot's name.\n"
1819 "-N: rename the snapshot's name as the new_ssname.\n"
1820 "-r: the remote shell used for communication with remote "
1821 "target, the default value is 'ssh'.\n");
1824 static int snapshot_modify_check(struct snapshot_instance *si)
1828 if (si->si_new_ssname &&
1829 strcmp(si->si_ssname, si->si_new_ssname) == 0) {
1830 fprintf(stderr, "The new snapshot's name is the same as "
1831 "the old one %s %s.\n",
1832 si->si_ssname, si->si_new_ssname);
1836 if (!si->si_new_ssname && !si->si_comment) {
1837 fprintf(stderr, "Miss options, nothing to be changed.\n");
1841 rc = mdt0_is_lustre_snapshot(si);
1842 if (!rc && si->si_new_ssname) {
1843 rc = target_is_mounted(si, si->si_mdt0, si->si_ssname);
1846 "snapshot %s is mounted, can't be renamed.\n",
1855 static int __snapshot_modify(struct snapshot_instance *si,
1856 struct list_head *head, __u64 xtime)
1858 struct snapshot_target *st;
1862 list_for_each_entry(st, head, st_list) {
1869 SNAPSHOT_ADD_LOG(si, "Can't fork for modify snapshot "
1870 "(%s|%s, <%s>) on the target "
1871 "(%s(%s):%x:%d): %s\n",
1872 si->si_ssname, si->si_new_ssname,
1873 si->si_comment, st->st_host,
1874 st->st_fhost ? st->st_fhost : "none",
1875 st->st_role, st->st_index,
1882 char cmd[MAX_BUF_SIZE * 5];
1885 memset(cmd, 0, sizeof(cmd));
1886 if (si->si_new_ssname && si->si_comment)
1887 snprintf(cmd, sizeof(cmd) - 1,
1888 DRSH" '"DIMPORT"; "DZFS" rename "
1889 DSSNAME" "DSSNAME" && "DZFS
1890 " set lustre:comment=\"%s\" "DSSNAME
1892 " set lustre:mtime=%llu "DSSNAME"'"
1894 PRSH(si, st, foreign), PIMPORT(st),
1895 PZFS(st), PSSNAME(si, st),
1896 PSS_NEW(si, st), PZFS(st),
1897 si->si_comment, PSS_NEW(si, st),
1898 PZFS(st), (unsigned long long)xtime,
1900 else if (si->si_new_ssname)
1901 snprintf(cmd, sizeof(cmd) - 1,
1902 DRSH" '"DIMPORT"; "DZFS
1903 " rename "DSSNAME" "DSSNAME" && "DZFS
1904 " set lustre:mtime=%llu "DSSNAME"'"
1906 PRSH(si, st, foreign), PIMPORT(st),
1907 PZFS(st), PSSNAME(si, st),
1908 PSS_NEW(si, st), PZFS(st),
1909 (unsigned long long)xtime,
1911 else if (si->si_comment)
1912 snprintf(cmd, sizeof(cmd) - 1,
1913 DRSH" '"DIMPORT"; "DZFS
1914 " set lustre:comment=\"%s\" "DSSNAME
1915 " && "DZFS " set lustre:mtime=%llu "
1916 DSSNAME"' 2>/dev/null",
1917 PRSH(si, st, foreign), PIMPORT(st),
1918 PZFS(st), si->si_comment,
1919 PSSNAME(si, st), PZFS(st),
1920 (unsigned long long)xtime,
1925 rc = snapshot_exec(cmd);
1927 SNAPSHOT_ADD_LOG(si, "Can't execute \"%s\" on "
1928 "target (%s:%x:%d): rc = %d\n",
1929 cmd, foreign ? st->st_fhost :
1930 st->st_host, st->st_role,
1932 if (!foreign && st->st_fhost) {
1939 } /* end of child */
1941 /* parent continue to run more snapshot commands in parallel. */
1948 static int snapshot_modify(struct snapshot_instance *si)
1954 rc = snapshot_modify_check(si);
1960 /* Modify snapshot on every MDT */
1961 rc = __snapshot_modify(si, &si->si_mdts_list, (__u64)tt);
1963 /* Modify snapshot on every OST */
1964 rc = __snapshot_modify(si, &si->si_osts_list, (__u64)tt);
1966 /* Wait for all children, even though part of them maybe failed */
1967 snapshot_wait(si, &rc1);
1969 return rc ? rc : rc1;
1972 int jt_snapshot_modify(int argc, char **argv)
1974 struct snapshot_instance *si;
1975 struct option long_opts[] = {
1976 { .val = 'c', .name = "comment", .has_arg = required_argument },
1977 { .val = 'F', .name = "fsname", .has_arg = required_argument },
1978 { .val = 'h', .name = "help", .has_arg = no_argument },
1979 { .val = 'n', .name = "name", .has_arg = required_argument },
1980 { .val = 'N', .name = "new", .has_arg = required_argument },
1981 { .val = 'r', .name = "rsh", .has_arg = required_argument },
1985 si = snapshot_init(argc, argv, long_opts, "c:F:hn:N:r:",
1986 snapshot_modify_usage, LOCK_EX, &rc);
1990 if (!si->si_ssname) {
1992 "Miss the snapshot name to be modified\n");
1993 snapshot_modify_usage();
1998 rc = snapshot_modify(si);
2001 "Can't modify the snapshot %s\n", si->si_ssname);
2002 SNAPSHOT_ADD_LOG(si, "Can't modify snapshot %s with "
2003 "name <%s>, comment <%s>: %d\n",
2004 si->si_ssname, si->si_new_ssname,
2005 si->si_comment, rc);
2007 SNAPSHOT_ADD_LOG(si, "Modify snapshot %s successfully "
2008 "with name <%s>, comment <%s>\n",
2009 si->si_ssname, si->si_new_ssname,
2017 static void snapshot_list_usage(void)
2020 "List the specified snapshot or all snapshots.\n"
2022 "snapshot_list [-d | --detail] "
2023 "<-F | --fsname fsname> [-h | --help] "
2024 "[-n | --name ssname] [-r | --rsh remote_shell]\n"
2026 "-d: list every piece for the specified snapshot.\n"
2027 "-F: the filesystem name.\n"
2028 "-h: for help information.\n"
2029 "-n: the snapshot's name.\n"
2030 "-r: the remote shell used for communication with remote "
2031 "target, the default value is 'ssh'.\n");
2034 static int snapshot_list_one(struct snapshot_instance *si,
2035 struct snapshot_target *st)
2037 char buf[MAX_BUF_SIZE * 3];
2043 memset(buf, 0, sizeof(buf));
2044 snprintf(buf, sizeof(buf) - 1,
2045 DRSH" \""DIMPORT"; "DZFS
2046 " get all "DSSNAME" | grep lustre: | grep local$ | "
2047 "awk '{ \\$1=\\\"\\\"; \\$NF=\\\"\\\"; print \\$0 }' | "
2048 "sed -e 's/^ //'\" 2>/dev/null",
2049 PRSH(si, st, foreign), PIMPORT(st), PZFS(st),
2051 fp = popen(buf, "r");
2053 SNAPSHOT_ADD_LOG(si, "Popen fail to list one: %s\n",
2058 if (si->si_detail && !foreign) {
2061 snapshot_role2name(name, st->st_role, st->st_index);
2062 printf("\nsnapshot_role: %s\n", name);
2065 while (snapshot_fgets(fp, buf, MAX_BUF_SIZE) != NULL) {
2070 if (strncmp(buf, "lustre:fsname",
2071 strlen("lustre:fsname")) == 0) {
2072 ptr = snapshot_first_skip_blank(buf);
2074 printf("snapshot_fsname: %s\n", ptr);
2078 if (strncmp(buf, "lustre:comment",
2079 strlen("lustre:comment")) == 0) {
2080 ptr = snapshot_first_skip_blank(buf);
2082 printf("comment: %s\n", ptr);
2086 if (strncmp(buf, "lustre:ctime",
2087 strlen("lustre:ctime")) == 0) {
2088 ptr = snapshot_first_skip_blank(buf);
2090 xtime = (__u64)strtoull(ptr, NULL, 10);
2091 printf("create_time: %s",
2092 ctime((time_t *)&xtime));
2097 if (strncmp(buf, "lustre:mtime",
2098 strlen("lustre:mtime")) == 0) {
2099 ptr = snapshot_first_skip_blank(buf);
2101 xtime = (__u64)strtoull(ptr, NULL, 10);
2102 printf("modify_time: %s",
2103 ctime((time_t *)&xtime));
2110 if (!foreign && st->st_fhost) {
2114 rc = target_is_mounted(si, st, si->si_ssname);
2116 printf("status: unknown\n");
2118 printf("status: not mount\n");
2120 printf("status: mounted\n");
2125 static int __snapshot_list(struct snapshot_instance *si,
2126 struct list_head *head)
2128 struct snapshot_target *st;
2131 list_for_each_entry(st, head, st_list) {
2134 rc1 = snapshot_list_one(si, st);
2135 if (rc1 < 0 || rc >= 0)
2142 static int snapshot_list(struct snapshot_instance *si)
2146 rc = snapshot_general_check(si);
2150 printf("\nfilesystem_name: %s\nsnapshot_name: %s\n",
2151 si->si_fsname, si->si_ssname);
2153 if (!si->si_detail) {
2154 rc = snapshot_list_one(si, si->si_mdt0);
2158 rc = __snapshot_list(si, &si->si_mdts_list);
2159 rc1 = __snapshot_list(si, &si->si_osts_list);
2164 return rc < 0 ? rc : 0;
2167 static int snapshot_list_all(struct snapshot_instance *si)
2169 struct snapshot_target *st = si->si_mdt0;
2170 struct list_sub_item {
2171 struct list_head lsi_list;
2175 struct list_head list_sub_items;
2176 struct list_sub_item *lsi;
2177 char buf[MAX_BUF_SIZE * 2];
2182 INIT_LIST_HEAD(&list_sub_items);
2184 memset(buf, 0, sizeof(buf));
2185 snprintf(buf, sizeof(buf) - 1,
2186 DRSH" \""DZFS" get -H -r lustre:magic "DFSNAME
2187 " | grep %s | awk '{ print \\$1 }' | cut -d@ -f2\" "
2189 PRSH(si, st, foreign), PZFS(st), PFSNAME(st),
2191 fp = popen(buf, "r");
2193 SNAPSHOT_ADD_LOG(si, "Popen fail to list ssnames: %s\n",
2198 while (snapshot_fgets(fp, buf, MAX_BUF_SIZE) != NULL) {
2199 int len = strlen(buf);
2202 lsi = malloc(len + 1 + sizeof(struct list_sub_item));
2204 SNAPSHOT_ADD_LOG(si, "NOT enough memory\n");
2209 memcpy(lsi->lsi_ssname, buf, len + 1);
2210 list_add(&lsi->lsi_list, &list_sub_items);
2214 if (!foreign && st->st_fhost) {
2218 while (!list_empty(&list_sub_items)) {
2219 lsi = list_first_entry(&list_sub_items,
2220 struct list_sub_item, lsi_list);
2221 list_del(&lsi->lsi_list);
2223 si->si_ssname = lsi->lsi_ssname;
2224 rc = snapshot_list(si);
2225 si->si_ssname = NULL;
2234 int jt_snapshot_list(int argc, char **argv)
2236 struct snapshot_instance *si;
2237 struct option long_opts[] = {
2238 { .val = 'd', .name = "detail", .has_arg = no_argument },
2239 { .val = 'F', .name = "fsname", .has_arg = required_argument },
2240 { .val = 'h', .name = "help", .has_arg = no_argument },
2241 { .val = 'n', .name = "name", .has_arg = required_argument },
2242 { .val = 'r', .name = "rsh", .has_arg = required_argument },
2246 si = snapshot_init(argc, argv, long_opts, "dF:hn:r:",
2247 snapshot_list_usage, LOCK_SH, &rc);
2252 rc = snapshot_list(si);
2254 rc = snapshot_list_all(si);
2258 "Can't list the snapshot %s\n", si->si_ssname);
2259 SNAPSHOT_ADD_LOG(si, "Can't list snapshot %s with detail "
2260 "<%s>: %d\n", si->si_ssname,
2261 si->si_detail ? "yes" : "no", rc);
2268 static void snapshot_mount_usage(void)
2271 "Mount the specified snapshot.\n"
2273 "snapshot_mount <-F | --fsname fsname> [-h | --help] "
2274 "<-n | --name ssname> "
2275 "[-r | --rsh remote_shell]\n"
2277 "-F: the filesystem name.\n"
2278 "-h: for help information.\n"
2279 "-n: the snapshot's name.\n"
2280 "-r: the remote shell used for communication with remote "
2281 "target, the default value is 'ssh'.\n");
2284 static int snapshot_mount_check(struct snapshot_instance *si, char *fsname,
2285 int fslen, bool *mgs_running)
2289 rc = mdt0_is_lustre_snapshot(si);
2293 rc = snapshot_get_fsname(si, fsname, fslen);
2297 rc = target_is_mounted(si, si->si_mgs, NULL);
2299 *mgs_running = true;
2306 static int snapshot_mount_target(struct snapshot_instance *si,
2307 struct snapshot_target *st, const char *optstr)
2309 char cmd[MAX_BUF_SIZE * 2];
2314 rc = target_is_mounted(si, st, si->si_ssname);
2321 memset(name, 0, sizeof(name));
2322 snapshot_role2name(name, st->st_role, st->st_index);
2324 memset(cmd, 0, sizeof(cmd));
2325 snprintf(cmd, sizeof(cmd) - 1,
2326 DRSH" '"DIMPORT"; mkdir -p /mnt/%s_%s && mount -t lustre "
2327 "-o rdonly_dev%s "DSSNAME" /mnt/%s_%s' 2>/dev/null",
2328 PRSH(si, st, foreign), PIMPORT(st), si->si_ssname, name,
2329 st != si->si_mdt0 ? "" : optstr, PSSNAME(si, st),
2330 si->si_ssname, name);
2331 rc = snapshot_exec(cmd);
2333 SNAPSHOT_ADD_LOG(si, "Can't execute \"%s\" on the target "
2334 "(%s:%x:%d): rc = %d\n",
2335 cmd, foreign ? st->st_fhost : st->st_host,
2336 st->st_role, st->st_index, rc);
2337 if (!foreign && st->st_fhost) {
2346 static int __snapshot_mount(struct snapshot_instance *si,
2347 struct list_head *head)
2349 struct snapshot_target *st;
2353 list_for_each_entry(st, head, st_list) {
2354 if (st == si->si_mgs || st == si->si_mdt0)
2363 SNAPSHOT_ADD_LOG(si, "Can't fork for mount snapshot "
2364 "%s on target (%s(%s):%x:%d): %s\n",
2365 si->si_ssname, st->st_host,
2366 st->st_fhost ? st->st_fhost : "none",
2367 st->st_role, st->st_index,
2374 rc = snapshot_mount_target(si, st, "");
2378 /* parent continue to run more snapshot commands in parallel. */
2385 static int __snapshot_umount(struct snapshot_instance *si,
2386 struct list_head *head);
2388 static int snapshot_mount(struct snapshot_instance *si)
2390 struct snapshot_target *st;
2396 bool mdt0_mounted = false;
2397 bool mgs_running = false;
2399 rc = snapshot_mount_check(si, fsname, sizeof(fsname), &mgs_running);
2402 "Can't mount the snapshot %s: %s\n",
2403 si->si_ssname, strerror(-rc));
2407 /* 1. MGS is not mounted yet, mount the MGS firstly */
2408 si->si_mgs->st_ignored = 0;
2409 si->si_mgs->st_pid = 0;
2411 rc = snapshot_mount_target(si, si->si_mgs, "");
2413 si->si_mgs->st_ignored = 1;
2419 "Can't mount the snapshot %s: %s\n",
2420 si->si_ssname, strerror(-rc));
2424 if (si->si_mgs == si->si_mdt0)
2425 mdt0_mounted = true;
2427 si->si_mgs->st_ignored = 1;
2430 /* 2. Mount MDT0 if it is not combined with the MGS. */
2431 if (!mdt0_mounted) {
2432 si->si_mdt0->st_ignored = 0;
2433 si->si_mdt0->st_pid = 0;
2434 rc = snapshot_mount_target(si, si->si_mdt0, ",nomgs");
2436 si->si_mdt0->st_ignored = 1;
2443 /* 3.1 Mount other MDTs in parallel */
2444 rc = __snapshot_mount(si, &si->si_mdts_list);
2446 /* 3.2 Mount OSTs in parallel */
2447 rc = __snapshot_mount(si, &si->si_osts_list);
2449 /* Wait for all children, even though part of them maybe failed */
2450 failed = snapshot_wait(si, &rc1);
2452 list_for_each_entry(st, &si->si_mdts_list, st_list) {
2453 if (!st->st_ignored)
2457 list_for_each_entry(st, &si->si_osts_list, st_list) {
2458 if (!st->st_ignored)
2466 __snapshot_umount(si, &si->si_mdts_list);
2467 __snapshot_umount(si, &si->si_osts_list);
2468 snapshot_wait(si, &rc2);
2472 "Can't mount the snapshot %s: %s\n",
2473 si->si_ssname, strerror(-rc));
2476 "%d of %d pieces of the snapshot %s "
2477 "can't be mounted: %s\n",
2478 failed, needed, si->si_ssname, strerror(-rc1));
2480 return rc ? rc : rc1;
2485 "The snapshot %s has already been mounted by other\n",
2490 fprintf(stdout, "mounted the snapshot %s with fsname %s\n",
2491 si->si_ssname, fsname);
2496 int jt_snapshot_mount(int argc, char **argv)
2498 struct snapshot_instance *si;
2499 struct option long_opts[] = {
2500 { .val = 'F', .name = "fsname", .has_arg = required_argument },
2501 { .val = 'h', .name = "help", .has_arg = no_argument },
2502 { .val = 'n', .name = "name", .has_arg = required_argument },
2503 { .val = 'r', .name = "rsh", .has_arg = required_argument },
2507 si = snapshot_init(argc, argv, long_opts, "F:hn:r:",
2508 snapshot_mount_usage, LOCK_EX, &rc);
2512 if (!si->si_ssname) {
2514 "Miss the snapshot name to be mounted\n");
2515 snapshot_mount_usage();
2520 rc = snapshot_mount(si);
2522 SNAPSHOT_ADD_LOG(si, "Can't mount snapshot %s: %d\n",
2525 SNAPSHOT_ADD_LOG(si, "The snapshot %s is mounted\n",
2533 static void snapshot_umount_usage(void)
2536 "Umount the specified snapshot.\n"
2538 "snapshot_umount <-F | --fsname fsname> [-h | --help] "
2539 "<-n | --name ssname> "
2540 "[-r | --rsh remote_shell]\n"
2542 "-F: the filesystem name.\n"
2543 "-h: for help information.\n"
2544 "-n: the snapshot's name.\n"
2545 "-r: the remote shell used for communication with remote "
2546 "target, the default value is 'ssh'.\n");
2549 static int __snapshot_umount(struct snapshot_instance *si,
2550 struct list_head *head)
2552 struct snapshot_target *st;
2556 list_for_each_entry(st, head, st_list) {
2563 SNAPSHOT_ADD_LOG(si, "Can't fork for umount snapshot "
2564 "%s on target (%s(%s):%x:%d): %s\n",
2565 si->si_ssname, st->st_host,
2566 st->st_fhost ? st->st_fhost : "none",
2567 st->st_role, st->st_index,
2574 char cmd[MAX_BUF_SIZE];
2577 rc = target_is_mounted(si, st, si->si_ssname);
2584 memset(cmd, 0, sizeof(cmd));
2585 snprintf(cmd, sizeof(cmd) - 1,
2586 DRSH" 'umount "DSSNAME"' 2>/dev/null",
2587 PRSH(si, st, foreign), PSSNAME(si, st));
2588 rc = snapshot_exec(cmd);
2589 if (rc && !foreign && st->st_fhost) {
2597 /* parent continue to run more snapshot commands in parallel. */
2604 static int snapshot_umount(struct snapshot_instance *si)
2606 struct snapshot_target *st;
2613 rc = snapshot_general_check(si);
2616 "Can't umount the snapshot %s: %s\n",
2617 si->si_ssname, strerror(-rc));
2621 rc = __snapshot_umount(si, &si->si_mdts_list);
2622 rc1 = __snapshot_umount(si, &si->si_osts_list);
2623 failed = snapshot_wait(si, &rc2);
2625 list_for_each_entry(st, &si->si_mdts_list, st_list) {
2626 if (!st->st_ignored)
2630 list_for_each_entry(st, &si->si_osts_list, st_list) {
2631 if (!st->st_ignored)
2637 "The snapshot %s has not been mounted\n",
2644 "%d of %d pieces of the snapshot %s "
2645 "can't be umounted: %s\n",
2646 failed, needed, si->si_ssname, strerror(-rc2));
2648 return rc ? rc : (rc1 ? rc1 : rc2);
2651 int jt_snapshot_umount(int argc, char **argv)
2653 struct snapshot_instance *si;
2654 struct option long_opts[] = {
2655 { .val = 'F', .name = "fsname", .has_arg = required_argument },
2656 { .val = 'h', .name = "help", .has_arg = no_argument },
2657 { .val = 'n', .name = "name", .has_arg = required_argument },
2658 { .val = 'r', .name = "rsh", .has_arg = required_argument },
2662 si = snapshot_init(argc, argv, long_opts, "F:hn:r:",
2663 snapshot_umount_usage, LOCK_EX, &rc);
2667 if (!si->si_ssname) {
2669 "Miss the snapshot name to be umounted\n");
2670 snapshot_umount_usage();
2675 rc = snapshot_umount(si);
2677 SNAPSHOT_ADD_LOG(si, "Can't umount snapshot %s: %d\n",
2680 SNAPSHOT_ADD_LOG(si, "the snapshot %s have been umounted\n",