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 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",
622 static void snapshot_unload_conf(struct snapshot_instance *si)
624 struct snapshot_target *st;
626 while (!list_empty(&si->si_mdts_list)) {
627 st = list_entry(si->si_mdts_list.next,
628 struct snapshot_target, st_list);
629 list_del(&st->st_list);
634 free(st->st_filesystem);
638 while (!list_empty(&si->si_osts_list)) {
639 st = list_entry(si->si_osts_list.next,
640 struct snapshot_target, st_list);
641 list_del(&st->st_list);
646 free(st->st_filesystem);
653 if (si->si_conf_fd >= 0) {
654 flock(si->si_conf_fd, LOCK_UN);
655 close(si->si_conf_fd);
660 static int snapshot_handle_string_option(char **dst, const char *option,
661 const char *opt_name)
663 if (*dst && *dst != snapshot_rsh_default) {
665 "%s option has been specified repeatedly.\n", opt_name);
669 *dst = strdup(option);
675 static void snapshot_fini(struct snapshot_instance *si)
677 snapshot_unload_conf(si);
680 fclose(si->si_log_fp);
682 if (si->si_rsh && si->si_rsh != snapshot_rsh_default)
688 if (si->si_new_ssname)
689 free(si->si_new_ssname);
691 free(si->si_comment);
696 static struct snapshot_instance *
697 snapshot_init(int argc, char * const argv[], const struct option *longopts,
698 const char *optstring, void (*usage)(void),
699 int lock_mode, int *err)
701 struct snapshot_instance *si;
706 si = malloc(sizeof(*si));
709 "No enough memory to initialize snapshot instance.\n");
714 memset(si, 0, sizeof(*si));
715 INIT_LIST_HEAD(&si->si_mdts_list);
716 INIT_LIST_HEAD(&si->si_osts_list);
717 si->si_rsh = (char *)snapshot_rsh_default;
719 si->si_timeout = BARRIER_TIMEOUT_DEFAULT;
720 si->si_barrier = true;
721 si->si_detail = false;
722 si->si_force = false;
724 while ((opt = getopt_long(argc, argv, optstring,
725 longopts, &idx)) != EOF) {
728 if (!optarg || strcmp(optarg, "on") == 0) {
729 si->si_barrier = true;
730 } else if (strcmp(optarg, "off") == 0) {
731 si->si_barrier = false;
739 *err = snapshot_handle_string_option(&si->si_comment,
745 si->si_detail = true;
751 *err = snapshot_handle_string_option(&si->si_fsname,
757 *err = snapshot_handle_string_option(&si->si_ssname,
763 *err = snapshot_handle_string_option(&si->si_new_ssname,
770 *err = snapshot_handle_string_option(&si->si_rsh,
777 si->si_timeout = atoi(optarg);
791 if (!si->si_fsname) {
792 fprintf(stderr, "The fsname must be specified\n");
798 if (strlen(si->si_fsname) > 8) {
799 fprintf(stderr, "Invalid fsname %s\n", si->si_fsname);
804 si->si_log_fp = fopen(SNAPSHOT_LOG, "a");
805 if (!si->si_log_fp) {
808 "Can't open the snapshot log file %s: %s\n",
809 SNAPSHOT_LOG, strerror(errno));
813 *err = snapshot_load_conf(si, lock_mode);
816 if (*err != 0 && si) {
824 static int __snapshot_wait(struct snapshot_instance *si,
825 struct list_head *head, int *err)
827 struct snapshot_target *st;
831 list_for_each_entry(st, head, st_list) {
835 rc = waitpid(st->st_pid, &st->st_status, 0);
837 SNAPSHOT_ADD_LOG(si, "Can't wait child (%d) operation "
838 "on the target <%s(%s):%x:%d>: %s\n",
839 st->st_pid, st->st_host,
840 st->st_fhost ? st->st_fhost : "none",
841 st->st_role, st->st_index,
848 /* continue to wait for next */
852 if (WIFEXITED(st->st_status)) {
853 rc = WEXITSTATUS(st->st_status);
858 st->st_ignored = true;
864 } else if (WIFSIGNALED(st->st_status)) {
865 SNAPSHOT_ADD_LOG(si, "The child (%d) operation on the "
866 "target <%s(%s):%x:%d> was killed by "
868 st->st_pid, st->st_host,
869 st->st_fhost ? st->st_fhost : "none",
870 st->st_role, st->st_index,
871 WTERMSIG(st->st_status));
876 SNAPSHOT_ADD_LOG(si, "The child (%d) operation on the "
877 "target <%s(%s):%x:%d> failed for "
879 st->st_pid, st->st_host,
880 st->st_fhost ? st->st_fhost : "none",
881 st->st_role, st->st_index);
893 static int snapshot_wait(struct snapshot_instance *si, int *err)
897 count = __snapshot_wait(si, &si->si_mdts_list, err);
898 count += __snapshot_wait(si, &si->si_osts_list, err);
903 static char *snapshot_first_skip_blank(char *buf)
907 ptr = strchr(buf, ' ');
909 ptr = strchr(buf, '\t');
914 while (*ptr == ' ' || *ptr == '\t')
923 static int mdt0_is_lustre_snapshot(struct snapshot_instance *si)
925 struct snapshot_target *st = si->si_mdt0;
926 char buf[MAX_BUF_SIZE * 3];
931 memset(buf, 0, sizeof(buf));
932 snprintf(buf, sizeof(buf) - 1,
933 DRSH" '"DIMPORT"; "DZFS
934 " get -H -o value lustre:magic "DSSNAME"' 2>/dev/null",
935 PRSH(si, st, foreign), PIMPORT(st), PZFS(st), PSSNAME(si, st));
937 fp = popen(buf, "r");
939 SNAPSHOT_ADD_LOG(si, "Popen fail to check snapshot "
940 "on mdt0: %s\n", strerror(errno));
944 if (snapshot_fgets(fp, buf, strlen(SNAPSHOT_MAGIC) + 1) == NULL) {
945 if (foreign || !st->st_fhost) {
952 } else if (strcmp(buf, SNAPSHOT_MAGIC) == 0) {
956 "The target %s is not Lustre snapshot "
957 "or it does not exists\n", si->si_ssname);
965 static int target_is_mounted(struct snapshot_instance *si,
966 struct snapshot_target *st, const char *ssname)
968 char buf[MAX_BUF_SIZE];
969 char fullname[MAX_BUF_SIZE];
976 memset(buf, 0, sizeof(buf));
977 snprintf(buf, sizeof(buf) - 1,
978 DRSH" 'mount' 2>/dev/null",
979 PRSH(si, st, foreign));
980 fp = popen(buf, "r");
982 SNAPSHOT_ADD_LOG(si, "Popen fail to check target mount: %s\n",
987 memset(fullname, 0, sizeof(fullname));
989 snprintf(fullname, sizeof(fullname) - 1,
991 PFSNAME(st), ssname);
993 snprintf(fullname, sizeof(fullname) - 1,
997 while (snapshot_fgets(fp, buf, sizeof(buf)) != NULL) {
998 ptr = strstr(buf, fullname);
1002 ptr += strlen(fullname) + 1; /* mount point */
1003 if (ptr >= buf + strlen(buf))
1006 ptr = strstr(ptr, "type lustre");
1014 if (!rc && !foreign && st->st_fhost) {
1021 static int snapshot_get_fsname(struct snapshot_instance *si,
1022 char *fsname, int fslen)
1024 struct snapshot_target *st = si->si_mdt0;
1025 char buf[MAX_BUF_SIZE * 3];
1031 memset(buf, 0, sizeof(buf));
1032 snprintf(buf, sizeof(buf) - 1,
1033 DRSH" '"DIMPORT"; "DZFS
1034 " get -H -o value lustre:fsname "DSSNAME"' 2>/dev/null",
1035 PRSH(si, st, foreign), PIMPORT(st), PZFS(st),
1037 fp = popen(buf, "r");
1039 SNAPSHOT_ADD_LOG(si, "Popen fail to get fsname: %s\n",
1044 if (snapshot_fgets(fp, fsname, fslen) == NULL)
1049 if (rc && !foreign && st->st_fhost) {
1056 static int snapshot_get_mgsnode(struct snapshot_instance *si,
1057 char *node, int size)
1059 char buf[MAX_BUF_SIZE * 2];
1060 struct snapshot_target *st;
1065 st = list_entry(si->si_osts_list.next, struct snapshot_target,
1069 memset(buf, 0, sizeof(buf));
1070 snprintf(buf, sizeof(buf) - 1,
1071 DRSH" '"DZFS" get -H -o value lustre:mgsnode "DFSNAME"'"
1072 " 2>/dev/null", PRSH(si, st, foreign), PZFS(st),
1074 fp = popen(buf, "r");
1076 SNAPSHOT_ADD_LOG(si, "Popen fail to get mgsnode: %s\n",
1081 if (snapshot_fgets(fp, node, size) == NULL)
1086 if (rc && !foreign && st->st_fhost) {
1094 static int snapshot_exists_check(struct snapshot_instance *si,
1095 struct snapshot_target *st)
1097 char buf[MAX_BUF_SIZE * 2];
1102 memset(buf, 0, sizeof(buf));
1103 snprintf(buf, sizeof(buf) - 1,
1104 DRSH" '"DZFS" list "DSSNAME"' 2>/dev/null",
1105 PRSH(si, st, foreign), PZFS(st), PSSNAME(si, st));
1106 fp = popen(buf, "r");
1108 SNAPSHOT_ADD_LOG(si, "Popen fail to create check: %s\n",
1113 if (snapshot_fgets(fp, buf, sizeof(buf)) != NULL)
1118 if (rc != -EEXIST && !foreign && st->st_fhost) {
1125 static int snapshot_general_check(struct snapshot_instance *si)
1127 return mdt0_is_lustre_snapshot(si);
1130 static void snapshot_create_usage(void)
1133 "Create snapshot for the given filesystem.\n"
1135 "snapshot_create [-b | --barrier [on | off]] "
1136 "[-c | --comment comment] "
1137 "<-F | --fsname fsname> "
1138 "[-h | --help] <-n | --name ssname> "
1139 "[-r | --rsh remote_shell]"
1140 "[-t | --timeout timeout]\n"
1142 "-b: set write barrier before creating snapshot, "
1143 "the default value is 'on'.\n"
1144 "-c: describe what the snapshot is for, and so on.\n"
1145 "-F: the filesystem name.\n"
1146 "-h: for help information.\n"
1147 "-n: the snapshot's name.\n"
1148 "-r: the remote shell used for communication with remote "
1149 "target, the default value is 'ssh'.\n"
1150 "-t: the life cycle (seconds) for write barrier, "
1151 "the default value is %d seconds.\n",
1152 BARRIER_TIMEOUT_DEFAULT);
1155 static int snapshot_create_check(struct snapshot_instance *si)
1159 rc = snapshot_exists_check(si, si->si_mdt0);
1161 fprintf(stderr, "The snapshot %s exists\n", si->si_ssname);
1166 static int snapshot_inherit_prop(struct snapshot_instance *si,
1167 struct snapshot_target *st,
1168 char *cmd, int size)
1170 char buf[MAX_BUF_SIZE * 3];
1176 memset(buf, 0, sizeof(buf));
1177 snprintf(buf, sizeof(buf) - 1,
1178 DRSH" \""DIMPORT"; "DZFS
1179 " get all "DFSNAME" | grep lustre: | grep local$ | "
1180 "awk '{ \\$1=\\\"\\\"; \\$NF=\\\"\\\"; print \\$0 }' | "
1181 "sed -e 's/^ //'\" 2>/dev/null",
1182 PRSH(si, st, foreign), PIMPORT(st), PZFS(st), PFSNAME(st));
1183 fp = popen(buf, "r");
1185 SNAPSHOT_ADD_LOG(si, "Popen fail to list one: %s\n",
1190 while (snapshot_fgets(fp, buf, MAX_BUF_SIZE) != NULL) {
1194 if (strncmp(buf, "lustre:fsname",
1195 strlen("lustre:fsname")) == 0)
1198 if (strncmp(buf, "lustre:magic",
1199 strlen("lustre:magic")) == 0)
1202 if (strncmp(buf, "lustre:ctime",
1203 strlen("lustre:ctime")) == 0)
1206 if (strncmp(buf, "lustre:mtime",
1207 strlen("lustre:mtime")) == 0)
1210 if (strncmp(buf, "lustre:comment",
1211 strlen("lustre:comment")) == 0)
1214 if (strncmp(buf, "lustre:svname",
1215 strlen("lustre:svname")) == 0)
1218 if (strncmp(buf, "lustre:mgsnode",
1219 strlen("lustre:mgsnode")) == 0)
1222 ptr = strchr(buf, ' ');
1224 ptr = strchr(buf, '\t');
1231 while (*ptr == ' ' || *ptr == '\t')
1237 end = strchr(ptr, ' ');
1239 end = strchr(buf, '\t');
1243 rc = scnprintf(cmd + len, size - len - 1,
1244 "-o %s=\"%s\" ", buf, ptr);
1252 if (!foreign && st->st_fhost) {
1259 static int __snapshot_create(struct snapshot_instance *si,
1260 struct list_head *head, const char *fsname,
1261 const char *mgsnode, __u64 xtime)
1263 struct snapshot_target *st;
1267 list_for_each_entry(st, head, st_list) {
1274 SNAPSHOT_ADD_LOG(si, "Can't fork for create snapshot "
1275 "(%s@%s <%s>) on the target "
1276 "(%s(%s):%x:%d): %s\n",
1277 fsname, si->si_ssname,
1278 si->si_comment, st->st_host,
1279 st->st_fhost ? st->st_fhost : "none",
1280 st->st_role, st->st_index,
1287 char cmd[MAX_BUF_SIZE];
1291 memset(cmd, 0, sizeof(cmd));
1292 len = scnprintf(cmd, sizeof(cmd) - 1,
1293 DRSH" '"DZFS" snapshot "
1294 "-o lustre:fsname=%s "
1295 "-o lustre:magic=%s "
1296 "-o lustre:ctime=%llu "
1297 "-o lustre:mtime=%llu ",
1298 PRSH(si, st, foreign), PZFS(st),
1299 fsname, SNAPSHOT_MAGIC,
1300 (unsigned long long)xtime,
1301 (unsigned long long)xtime);
1305 if (si->si_comment) {
1306 rc = scnprintf(cmd + len, sizeof(cmd) - len - 1,
1307 "-o lustre:comment=\"%s\" ",
1315 /* Make the inherited properties as local ones,
1316 * then even if others changed (or removed) the
1317 * property of the parent dataset, the snapshot
1318 * will not be affected. */
1319 rc = snapshot_inherit_prop(si, st, cmd + len,
1320 MAX_BUF_SIZE - len - 1);
1322 SNAPSHOT_ADD_LOG(si, "Can't filter property on "
1323 "target (%s:%x:%d): rc = %d\n",
1324 foreign ? st->st_fhost :
1325 st->st_host, st->st_role,
1331 if (st->st_role & SR_OST)
1332 rc = snprintf(cmd + len, sizeof(cmd) - len - 1,
1333 "-o lustre:svname=%s-OST%04x "
1334 "-o lustre:mgsnode=%s "DSSNAME"'"
1336 fsname, st->st_index, mgsnode,
1338 else if (!(st->st_role & SR_MGS) ||
1339 /* MGS is on MDT0 */
1340 si->si_mdt0 == si->si_mgs)
1341 rc = snprintf(cmd + len, sizeof(cmd) - len - 1,
1342 "-o lustre:svname=%s-MDT%04x "
1343 "-o lustre:mgsnode=%s "DSSNAME"'"
1345 fsname, st->st_index, mgsnode,
1349 rc = snprintf(cmd + len, sizeof(cmd) - len - 1,
1350 DSSNAME"' 2>/dev/null",
1355 rc = snapshot_exec(cmd);
1357 SNAPSHOT_ADD_LOG(si, "Can't execute \"%s\" on "
1358 "target (%s:%x:%d): rc = %d\n",
1359 cmd, foreign ? st->st_fhost :
1360 st->st_host, st->st_role,
1362 if (!foreign && st->st_fhost) {
1369 } /* end of child */
1371 /* parent continue to run more snapshot commands in parallel. */
1378 static int __snapshot_destroy(struct snapshot_instance *si,
1379 struct list_head *head);
1381 static int snapshot_create(struct snapshot_instance *si)
1384 char buf[MAX_BUF_SIZE];
1391 rc = snapshot_create_check(si);
1395 rc = gettimeofday(&tv, NULL);
1399 srandom(tv.tv_usec);
1400 snprintf(new_fsname, sizeof(new_fsname), "%08x", (__u32)random());
1402 rc = snapshot_get_mgsnode(si, buf, sizeof(buf));
1406 __argv[1] = si->si_fsname;
1407 /* 1. Get barrier */
1408 if (si->si_barrier) {
1411 memset(tbuf, 0, sizeof(tbuf));
1412 snprintf(tbuf, 7, "%u", si->si_timeout);
1413 __argv[0] = "barrier_freeze";
1415 rc = jt_barrier_freeze(3, __argv);
1417 SNAPSHOT_ADD_LOG(si, "Can't set barrier within %u "
1418 "seconds on %s: rc = %d\n",
1419 si->si_timeout, si->si_fsname, rc);
1425 /* 2. Fork config llog on MGS */
1426 __argv[0] = "fork_lcfg";
1427 __argv[2] = new_fsname;
1428 rc = jt_lcfg_fork(3, __argv);
1430 SNAPSHOT_ADD_LOG(si, "Can't fork config log for create "
1431 "snapshot %s from %s to %s: rc = %d\n",
1432 si->si_ssname, si->si_fsname, new_fsname, rc);
1436 /* 3.1 Create snapshot on every MDT */
1437 rc = __snapshot_create(si, &si->si_mdts_list, new_fsname, buf,
1440 /* 3.2 Create snapshot on every OST */
1441 rc = __snapshot_create(si, &si->si_osts_list, new_fsname, buf,
1444 /* 4. Wait for all children, even though part of them maybe failed */
1445 snapshot_wait(si, &rc1);
1448 /* 5. Put barrier */
1449 if (si->si_barrier) {
1451 struct barrier_ctl bc;
1453 rc = __jt_barrier_stat(__argv[1], &bc);
1455 SNAPSHOT_ADD_LOG(si, "Can't get barrier status "
1458 } else if (bc.bc_status != BS_FROZEN ||
1459 bc.bc_timeout <= 0) {
1460 SNAPSHOT_ADD_LOG(si, "The barrier expired "
1461 "on %s\n", si->si_fsname);
1466 __argv[0] = "barrier_thaw";
1467 rc2 = jt_barrier_thaw(2, __argv);
1469 SNAPSHOT_ADD_LOG(si, "Can't release barrier on %s: "
1470 "rc = %d\n", si->si_fsname, rc2);
1475 si->si_force = true;
1476 __snapshot_destroy(si, &si->si_osts_list);
1477 __snapshot_destroy(si, &si->si_mdts_list);
1478 snapshot_wait(si, &rc2);
1480 __argv[0] = "erase_lcfg";
1481 __argv[1] = new_fsname;
1483 jt_lcfg_erase(3, __argv);
1486 return rc ? rc : (rc1 ? rc1 : rc2);
1489 int jt_snapshot_create(int argc, char **argv)
1491 struct snapshot_instance *si;
1492 struct option long_opts[] = {
1493 { .val = 'b', .name = "barrier", .has_arg = optional_argument },
1494 { .val = 'c', .name = "comment", .has_arg = required_argument },
1495 { .val = 'F', .name = "fsname", .has_arg = required_argument },
1496 { .val = 'h', .name = "help", .has_arg = no_argument },
1497 { .val = 'n', .name = "name", .has_arg = required_argument },
1498 { .val = 'r', .name = "rsh", .has_arg = required_argument },
1499 { .val = 't', .name = "timeout", .has_arg = required_argument },
1503 si = snapshot_init(argc, argv, long_opts, "b::c:F:hn:r:t:",
1504 snapshot_create_usage, LOCK_EX, &rc);
1508 if (!si->si_ssname) {
1510 "Miss the snapshot name to be created\n");
1511 snapshot_create_usage();
1516 rc = snapshot_create(si);
1519 "Can't create the snapshot %s\n", si->si_ssname);
1520 SNAPSHOT_ADD_LOG(si, "Can't create snapshot %s with "
1521 "comment <%s> barrier <%s>, timeout "
1523 si->si_ssname, si->si_comment,
1524 si->si_barrier ? "enable" : "disable",
1525 si->si_barrier ? si->si_timeout : -1, rc);
1527 SNAPSHOT_ADD_LOG(si, "Create snapshot %s successfully "
1528 "with comment <%s>, barrier <%s>, "
1530 si->si_ssname, si->si_comment,
1531 si->si_barrier ? "enable" : "disable",
1532 si->si_barrier ? si->si_timeout : -1);
1539 static void snapshot_destroy_usage(void)
1542 "Destroy the specified snapshot.\n"
1544 "snapshot_destroy [-f | --force] "
1545 "<-F | --fsname fsname> [-h | --help] "
1546 "<-n | --name ssname> "
1547 "[-r | --rsh remote_shell]\n"
1549 "-f: destroy the snapshot by force.\n"
1550 "-F: the filesystem name.\n"
1551 "-h: for help information.\n"
1552 "-n: the snapshot's name.\n"
1553 "-r: the remote shell used for communication with remote "
1554 "target, the default value is 'ssh'.\n");
1557 static int snapshot_destroy_check(struct snapshot_instance *si)
1559 struct list_head *head = &si->si_osts_list;
1560 struct snapshot_target *st;
1565 list_for_each_entry(st, head, st_list) {
1572 SNAPSHOT_ADD_LOG(si, "Can't fork for check snapshot "
1573 "%s on the target (%s(%s):%x:%d): "
1575 si->si_ssname, st->st_host,
1576 st->st_fhost ? st->st_fhost : "none",
1577 st->st_role, st->st_index,
1584 rc = snapshot_exists_check(si, st);
1586 /* The snapshot piece does not exist */
1589 exit(rc == -EEXIST ? 0: rc);
1590 } /* end of child */
1592 /* parent continue to run more snapshot commands in parallel. */
1596 if (head == &si->si_osts_list) {
1597 head = &si->si_mdts_list;
1601 snapshot_wait(si, &rc);
1605 head = &si->si_osts_list;
1608 list_for_each_entry(st, head, st_list) {
1609 if (st->st_ignored && !si->si_force) {
1612 snapshot_role2name(name, st->st_role, st->st_index);
1614 "Miss snapshot piece on the %s. Use '-f' "
1615 "option if want to destroy it by force.\n",
1622 if (head == &si->si_osts_list) {
1623 head = &si->si_mdts_list;
1628 rc = snapshot_general_check(si);
1633 static int __snapshot_destroy(struct snapshot_instance *si,
1634 struct list_head *head)
1636 struct snapshot_target *st;
1640 list_for_each_entry(st, head, st_list) {
1649 SNAPSHOT_ADD_LOG(si, "Can't fork for destroy snapshot "
1650 "%s on the target (%s(%s):%x:%d): "
1652 si->si_ssname, st->st_host,
1653 st->st_fhost ? st->st_fhost : "none",
1654 st->st_role, st->st_index,
1661 char cmd[MAX_BUF_SIZE * 2];
1664 memset(cmd, 0, sizeof(cmd));
1666 snprintf(cmd, sizeof(cmd) - 1,
1667 DRSH" 'umount -f "DSSNAME
1668 " > /dev/null 2>&1; "DZFS
1669 " destroy -f "DSSNAME"'"
1671 PRSH(si, st, foreign),
1672 PSSNAME(si, st), PZFS(st),
1675 snprintf(cmd, sizeof(cmd) - 1,
1676 DRSH" '"DZFS" destroy "DSSNAME"'"
1678 PRSH(si, st, foreign), PZFS(st),
1680 rc = snapshot_exec(cmd);
1682 SNAPSHOT_ADD_LOG(si, "Can't execute \"%s\" on "
1683 "target (%s:%x:%d): rc = %d\n",
1684 cmd, foreign ? st->st_fhost :
1685 st->st_host, st->st_role,
1687 if (!foreign && st->st_fhost) {
1694 } /* end of child */
1696 /* parent continue to run more snapshot commands in parallel. */
1703 static int snapshot_destroy(struct snapshot_instance *si)
1711 rc = snapshot_destroy_check(si);
1715 rc = snapshot_get_fsname(si, fsname, sizeof(fsname));
1719 /* 1.1 Destroy snapshot on every OST */
1720 rc = __snapshot_destroy(si, &si->si_osts_list);
1721 if (!si->si_force) {
1725 __snapshot_wait(si, &si->si_osts_list, &rc);
1730 /* 1.2 Destroy snapshot on every MDT */
1731 rc1 = __snapshot_destroy(si, &si->si_mdts_list);
1733 /* 2 Wait for all children, even though part of them maybe failed */
1734 snapshot_wait(si, &rc2);
1735 if (rc2 == -ENOENT && si->si_force)
1738 /* 3. Erase config llog from MGS */
1739 if ((!rc && !rc1 && !rc2) || si->si_force) {
1742 __argv[0] = "erase_lcfg";
1745 rc3 = jt_lcfg_erase(3, __argv);
1746 if (rc3 && errno == ENOENT)
1749 SNAPSHOT_ADD_LOG(si, "Can't erase config for destroy "
1750 "snapshot %s, fsname %s: rc = %d\n",
1751 si->si_ssname, fsname, rc3);
1754 return rc ? rc : (rc1 ? rc1 : (rc2 ? rc2 : rc3));
1757 int jt_snapshot_destroy(int argc, char **argv)
1759 struct snapshot_instance *si;
1760 struct option long_opts[] = {
1761 { .val = 'f', .name = "force", .has_arg = no_argument },
1762 { .val = 'F', .name = "fsname", .has_arg = required_argument },
1763 { .val = 'h', .name = "help", .has_arg = no_argument },
1764 { .val = 'n', .name = "name", .has_arg = required_argument },
1765 { .val = 'r', .name = "rsh", .has_arg = required_argument },
1769 si = snapshot_init(argc, argv, long_opts, "fF:hn:r:",
1770 snapshot_destroy_usage, LOCK_EX, &rc);
1774 if (!si->si_ssname) {
1776 "Miss the snapshot name to be destroyed\n");
1777 snapshot_destroy_usage();
1782 rc = snapshot_destroy(si);
1785 "Can't destroy the snapshot %s\n", si->si_ssname);
1786 SNAPSHOT_ADD_LOG(si, "Can't destroy snapshot %s with "
1787 "force <%s>: %d\n", si->si_ssname,
1788 si->si_force ? "enable" : "disable", rc);
1790 SNAPSHOT_ADD_LOG(si, "Destroy snapshot %s successfully "
1791 "with force <%s>\n", si->si_ssname,
1792 si->si_force ? "enable" : "disable");
1799 static void snapshot_modify_usage(void)
1802 "Change the specified snapshot's name and/or comment.\n"
1804 "snapshot_modify [-c | --comment comment] "
1805 "<-F | --fsname fsname> [-h | --help] "
1806 "<-n | --name ssname> [-N | --new new_ssname] "
1807 "[-r | --rsh remote_shell]\n"
1809 "-c: update the snapshot's comment.\n"
1810 "-F: the filesystem name.\n"
1811 "-h: for help information.\n"
1812 "-n: the snapshot's name.\n"
1813 "-N: rename the snapshot's name as the new_ssname.\n"
1814 "-r: the remote shell used for communication with remote "
1815 "target, the default value is 'ssh'.\n");
1818 static int snapshot_modify_check(struct snapshot_instance *si)
1822 if (si->si_new_ssname &&
1823 strcmp(si->si_ssname, si->si_new_ssname) == 0) {
1824 fprintf(stderr, "The new snapshot's name is the same as "
1825 "the old one %s %s.\n",
1826 si->si_ssname, si->si_new_ssname);
1830 if (!si->si_new_ssname && !si->si_comment) {
1831 fprintf(stderr, "Miss options, nothing to be changed.\n");
1835 rc = mdt0_is_lustre_snapshot(si);
1836 if (!rc && si->si_new_ssname) {
1837 rc = target_is_mounted(si, si->si_mdt0, si->si_ssname);
1840 "snapshot %s is mounted, can't be renamed.\n",
1849 static int __snapshot_modify(struct snapshot_instance *si,
1850 struct list_head *head, __u64 xtime)
1852 struct snapshot_target *st;
1856 list_for_each_entry(st, head, st_list) {
1863 SNAPSHOT_ADD_LOG(si, "Can't fork for modify snapshot "
1864 "(%s|%s, <%s>) on the target "
1865 "(%s(%s):%x:%d): %s\n",
1866 si->si_ssname, si->si_new_ssname,
1867 si->si_comment, st->st_host,
1868 st->st_fhost ? st->st_fhost : "none",
1869 st->st_role, st->st_index,
1876 char cmd[MAX_BUF_SIZE * 5];
1879 memset(cmd, 0, sizeof(cmd));
1880 if (si->si_new_ssname && si->si_comment)
1881 snprintf(cmd, sizeof(cmd) - 1,
1882 DRSH" '"DIMPORT"; "DZFS" rename "
1883 DSSNAME" "DSSNAME" && "DZFS
1884 " set lustre:comment=\"%s\" "DSSNAME
1886 " set lustre:mtime=%llu "DSSNAME"'"
1888 PRSH(si, st, foreign), PIMPORT(st),
1889 PZFS(st), PSSNAME(si, st),
1890 PSS_NEW(si, st), PZFS(st),
1891 si->si_comment, PSS_NEW(si, st),
1892 PZFS(st), (unsigned long long)xtime,
1894 else if (si->si_new_ssname)
1895 snprintf(cmd, sizeof(cmd) - 1,
1896 DRSH" '"DIMPORT"; "DZFS
1897 " rename "DSSNAME" "DSSNAME" && "DZFS
1898 " set lustre:mtime=%llu "DSSNAME"'"
1900 PRSH(si, st, foreign), PIMPORT(st),
1901 PZFS(st), PSSNAME(si, st),
1902 PSS_NEW(si, st), PZFS(st),
1903 (unsigned long long)xtime,
1905 else if (si->si_comment)
1906 snprintf(cmd, sizeof(cmd) - 1,
1907 DRSH" '"DIMPORT"; "DZFS
1908 " set lustre:comment=\"%s\" "DSSNAME
1909 " && "DZFS " set lustre:mtime=%llu "
1910 DSSNAME"' 2>/dev/null",
1911 PRSH(si, st, foreign), PIMPORT(st),
1912 PZFS(st), si->si_comment,
1913 PSSNAME(si, st), PZFS(st),
1914 (unsigned long long)xtime,
1919 rc = snapshot_exec(cmd);
1921 SNAPSHOT_ADD_LOG(si, "Can't execute \"%s\" on "
1922 "target (%s:%x:%d): rc = %d\n",
1923 cmd, foreign ? st->st_fhost :
1924 st->st_host, st->st_role,
1926 if (!foreign && st->st_fhost) {
1933 } /* end of child */
1935 /* parent continue to run more snapshot commands in parallel. */
1942 static int snapshot_modify(struct snapshot_instance *si)
1948 rc = snapshot_modify_check(si);
1954 /* Modify snapshot on every MDT */
1955 rc = __snapshot_modify(si, &si->si_mdts_list, (__u64)tt);
1957 /* Modify snapshot on every OST */
1958 rc = __snapshot_modify(si, &si->si_osts_list, (__u64)tt);
1960 /* Wait for all children, even though part of them maybe failed */
1961 snapshot_wait(si, &rc1);
1963 return rc ? rc : rc1;
1966 int jt_snapshot_modify(int argc, char **argv)
1968 struct snapshot_instance *si;
1969 struct option long_opts[] = {
1970 { .val = 'c', .name = "comment", .has_arg = required_argument },
1971 { .val = 'F', .name = "fsname", .has_arg = required_argument },
1972 { .val = 'h', .name = "help", .has_arg = no_argument },
1973 { .val = 'n', .name = "name", .has_arg = required_argument },
1974 { .val = 'N', .name = "new", .has_arg = required_argument },
1975 { .val = 'r', .name = "rsh", .has_arg = required_argument },
1979 si = snapshot_init(argc, argv, long_opts, "c:F:hn:N:r:",
1980 snapshot_modify_usage, LOCK_EX, &rc);
1984 if (!si->si_ssname) {
1986 "Miss the snapshot name to be modified\n");
1987 snapshot_modify_usage();
1992 rc = snapshot_modify(si);
1995 "Can't modify the snapshot %s\n", si->si_ssname);
1996 SNAPSHOT_ADD_LOG(si, "Can't modify snapshot %s with "
1997 "name <%s>, comment <%s>: %d\n",
1998 si->si_ssname, si->si_new_ssname,
1999 si->si_comment, rc);
2001 SNAPSHOT_ADD_LOG(si, "Modify snapshot %s successfully "
2002 "with name <%s>, comment <%s>\n",
2003 si->si_ssname, si->si_new_ssname,
2011 static void snapshot_list_usage(void)
2014 "List the specified snapshot or all snapshots.\n"
2016 "snapshot_list [-d | --detail] "
2017 "<-F | --fsname fsname> [-h | --help] "
2018 "[-n | --name ssname] [-r | --rsh remote_shell]\n"
2020 "-d: list every piece for the specified snapshot.\n"
2021 "-F: the filesystem name.\n"
2022 "-h: for help information.\n"
2023 "-n: the snapshot's name.\n"
2024 "-r: the remote shell used for communication with remote "
2025 "target, the default value is 'ssh'.\n");
2028 static int snapshot_list_one(struct snapshot_instance *si,
2029 struct snapshot_target *st)
2031 char buf[MAX_BUF_SIZE * 3];
2037 memset(buf, 0, sizeof(buf));
2038 snprintf(buf, sizeof(buf) - 1,
2039 DRSH" \""DIMPORT"; "DZFS
2040 " get all "DSSNAME" | grep lustre: | grep local$ | "
2041 "awk '{ \\$1=\\\"\\\"; \\$NF=\\\"\\\"; print \\$0 }' | "
2042 "sed -e 's/^ //'\" 2>/dev/null",
2043 PRSH(si, st, foreign), PIMPORT(st), PZFS(st),
2045 fp = popen(buf, "r");
2047 SNAPSHOT_ADD_LOG(si, "Popen fail to list one: %s\n",
2052 if (si->si_detail && !foreign) {
2055 snapshot_role2name(name, st->st_role, st->st_index);
2056 printf("\nsnapshot_role: %s\n", name);
2059 while (snapshot_fgets(fp, buf, MAX_BUF_SIZE) != NULL) {
2064 if (strncmp(buf, "lustre:fsname",
2065 strlen("lustre:fsname")) == 0) {
2066 ptr = snapshot_first_skip_blank(buf);
2068 printf("snapshot_fsname: %s\n", ptr);
2072 if (strncmp(buf, "lustre:comment",
2073 strlen("lustre:comment")) == 0) {
2074 ptr = snapshot_first_skip_blank(buf);
2076 printf("comment: %s\n", ptr);
2080 if (strncmp(buf, "lustre:ctime",
2081 strlen("lustre:ctime")) == 0) {
2082 ptr = snapshot_first_skip_blank(buf);
2084 xtime = (__u64)strtoull(ptr, NULL, 10);
2085 printf("create_time: %s",
2086 ctime((time_t *)&xtime));
2091 if (strncmp(buf, "lustre:mtime",
2092 strlen("lustre:mtime")) == 0) {
2093 ptr = snapshot_first_skip_blank(buf);
2095 xtime = (__u64)strtoull(ptr, NULL, 10);
2096 printf("modify_time: %s",
2097 ctime((time_t *)&xtime));
2104 if (!foreign && st->st_fhost) {
2108 rc = target_is_mounted(si, st, si->si_ssname);
2110 printf("status: unknown\n");
2112 printf("status: not mount\n");
2114 printf("status: mounted\n");
2119 static int __snapshot_list(struct snapshot_instance *si,
2120 struct list_head *head)
2122 struct snapshot_target *st;
2125 list_for_each_entry(st, head, st_list) {
2128 rc1 = snapshot_list_one(si, st);
2129 if (rc1 < 0 || rc >= 0)
2136 static int snapshot_list(struct snapshot_instance *si)
2140 rc = snapshot_general_check(si);
2144 printf("\nfilesystem_name: %s\nsnapshot_name: %s\n",
2145 si->si_fsname, si->si_ssname);
2147 if (!si->si_detail) {
2148 rc = snapshot_list_one(si, si->si_mdt0);
2152 rc = __snapshot_list(si, &si->si_mdts_list);
2153 rc1 = __snapshot_list(si, &si->si_osts_list);
2158 return rc < 0 ? rc : 0;
2161 static int snapshot_list_all(struct snapshot_instance *si)
2163 struct snapshot_target *st = si->si_mdt0;
2164 struct list_sub_item {
2165 struct list_head lsi_list;
2169 struct list_head list_sub_items;
2170 struct list_sub_item *lsi;
2171 char buf[MAX_BUF_SIZE * 2];
2176 INIT_LIST_HEAD(&list_sub_items);
2178 memset(buf, 0, sizeof(buf));
2179 snprintf(buf, sizeof(buf) - 1,
2180 DRSH" \""DZFS" get -H -r lustre:magic "DFSNAME
2181 " | grep %s | awk '{ print \\$1 }' | cut -d@ -f2\" "
2183 PRSH(si, st, foreign), PZFS(st), PFSNAME(st),
2185 fp = popen(buf, "r");
2187 SNAPSHOT_ADD_LOG(si, "Popen fail to list ssnames: %s\n",
2192 while (snapshot_fgets(fp, buf, MAX_BUF_SIZE) != NULL) {
2193 int len = strlen(buf);
2196 lsi = malloc(len + 1 + sizeof(struct list_sub_item));
2198 SNAPSHOT_ADD_LOG(si, "NOT enough memory\n");
2203 memcpy(lsi->lsi_ssname, buf, len + 1);
2204 list_add(&lsi->lsi_list, &list_sub_items);
2208 if (!foreign && st->st_fhost) {
2212 while (!list_empty(&list_sub_items)) {
2213 lsi = list_entry(list_sub_items.next,
2214 struct list_sub_item, lsi_list);
2215 list_del(&lsi->lsi_list);
2217 si->si_ssname = lsi->lsi_ssname;
2218 rc = snapshot_list(si);
2219 si->si_ssname = NULL;
2228 int jt_snapshot_list(int argc, char **argv)
2230 struct snapshot_instance *si;
2231 struct option long_opts[] = {
2232 { .val = 'd', .name = "detail", .has_arg = no_argument },
2233 { .val = 'F', .name = "fsname", .has_arg = required_argument },
2234 { .val = 'h', .name = "help", .has_arg = no_argument },
2235 { .val = 'n', .name = "name", .has_arg = required_argument },
2236 { .val = 'r', .name = "rsh", .has_arg = required_argument },
2240 si = snapshot_init(argc, argv, long_opts, "dF:hn:r:",
2241 snapshot_list_usage, LOCK_SH, &rc);
2246 rc = snapshot_list(si);
2248 rc = snapshot_list_all(si);
2252 "Can't list the snapshot %s\n", si->si_ssname);
2253 SNAPSHOT_ADD_LOG(si, "Can't list snapshot %s with detail "
2254 "<%s>: %d\n", si->si_ssname,
2255 si->si_detail ? "yes" : "no", rc);
2262 static void snapshot_mount_usage(void)
2265 "Mount the specified snapshot.\n"
2267 "snapshot_mount <-F | --fsname fsname> [-h | --help] "
2268 "<-n | --name ssname> "
2269 "[-r | --rsh remote_shell]\n"
2271 "-F: the filesystem name.\n"
2272 "-h: for help information.\n"
2273 "-n: the snapshot's name.\n"
2274 "-r: the remote shell used for communication with remote "
2275 "target, the default value is 'ssh'.\n");
2278 static int snapshot_mount_check(struct snapshot_instance *si, char *fsname,
2279 int fslen, bool *mgs_running)
2283 rc = mdt0_is_lustre_snapshot(si);
2287 rc = snapshot_get_fsname(si, fsname, fslen);
2291 rc = target_is_mounted(si, si->si_mgs, NULL);
2293 *mgs_running = true;
2300 static int snapshot_mount_target(struct snapshot_instance *si,
2301 struct snapshot_target *st, const char *optstr)
2303 char cmd[MAX_BUF_SIZE * 2];
2308 rc = target_is_mounted(si, st, si->si_ssname);
2315 memset(name, 0, sizeof(name));
2316 snapshot_role2name(name, st->st_role, st->st_index);
2318 memset(cmd, 0, sizeof(cmd));
2319 snprintf(cmd, sizeof(cmd) - 1,
2320 DRSH" '"DIMPORT"; mkdir -p /mnt/%s_%s && mount -t lustre "
2321 "-o rdonly_dev%s "DSSNAME" /mnt/%s_%s' 2>/dev/null",
2322 PRSH(si, st, foreign), PIMPORT(st), si->si_ssname, name,
2323 st != si->si_mdt0 ? "" : optstr, PSSNAME(si, st),
2324 si->si_ssname, name);
2325 rc = snapshot_exec(cmd);
2327 SNAPSHOT_ADD_LOG(si, "Can't execute \"%s\" on the target "
2328 "(%s:%x:%d): rc = %d\n",
2329 cmd, foreign ? st->st_fhost : st->st_host,
2330 st->st_role, st->st_index, rc);
2331 if (!foreign && st->st_fhost) {
2340 static int __snapshot_mount(struct snapshot_instance *si,
2341 struct list_head *head)
2343 struct snapshot_target *st;
2347 list_for_each_entry(st, head, st_list) {
2348 if (st == si->si_mgs || st == si->si_mdt0)
2357 SNAPSHOT_ADD_LOG(si, "Can't fork for mount snapshot "
2358 "%s on target (%s(%s):%x:%d): %s\n",
2359 si->si_ssname, st->st_host,
2360 st->st_fhost ? st->st_fhost : "none",
2361 st->st_role, st->st_index,
2368 rc = snapshot_mount_target(si, st, "");
2372 /* parent continue to run more snapshot commands in parallel. */
2379 static int __snapshot_umount(struct snapshot_instance *si,
2380 struct list_head *head);
2382 static int snapshot_mount(struct snapshot_instance *si)
2384 struct snapshot_target *st;
2390 bool mdt0_mounted = false;
2391 bool mgs_running = false;
2393 rc = snapshot_mount_check(si, fsname, sizeof(fsname), &mgs_running);
2396 "Can't mount the snapshot %s: %s\n",
2397 si->si_ssname, strerror(-rc));
2401 /* 1. MGS is not mounted yet, mount the MGS firstly */
2402 si->si_mgs->st_ignored = 0;
2403 si->si_mgs->st_pid = 0;
2405 rc = snapshot_mount_target(si, si->si_mgs, "");
2407 si->si_mgs->st_ignored = 1;
2413 "Can't mount the snapshot %s: %s\n",
2414 si->si_ssname, strerror(-rc));
2418 if (si->si_mgs == si->si_mdt0)
2419 mdt0_mounted = true;
2421 si->si_mgs->st_ignored = 1;
2424 /* 2. Mount MDT0 if it is not combined with the MGS. */
2425 if (!mdt0_mounted) {
2426 si->si_mdt0->st_ignored = 0;
2427 si->si_mdt0->st_pid = 0;
2428 rc = snapshot_mount_target(si, si->si_mdt0, ",nomgs");
2430 si->si_mdt0->st_ignored = 1;
2437 /* 3.1 Mount other MDTs in parallel */
2438 rc = __snapshot_mount(si, &si->si_mdts_list);
2440 /* 3.2 Mount OSTs in parallel */
2441 rc = __snapshot_mount(si, &si->si_osts_list);
2443 /* Wait for all children, even though part of them maybe failed */
2444 failed = snapshot_wait(si, &rc1);
2446 list_for_each_entry(st, &si->si_mdts_list, st_list) {
2447 if (!st->st_ignored)
2451 list_for_each_entry(st, &si->si_osts_list, st_list) {
2452 if (!st->st_ignored)
2460 __snapshot_umount(si, &si->si_mdts_list);
2461 __snapshot_umount(si, &si->si_osts_list);
2462 snapshot_wait(si, &rc2);
2466 "Can't mount the snapshot %s: %s\n",
2467 si->si_ssname, strerror(-rc));
2470 "%d of %d pieces of the snapshot %s "
2471 "can't be mounted: %s\n",
2472 failed, needed, si->si_ssname, strerror(-rc1));
2474 return rc ? rc : rc1;
2479 "The snapshot %s has already been mounted by other\n",
2484 fprintf(stdout, "mounted the snapshot %s with fsname %s\n",
2485 si->si_ssname, fsname);
2490 int jt_snapshot_mount(int argc, char **argv)
2492 struct snapshot_instance *si;
2493 struct option long_opts[] = {
2494 { .val = 'F', .name = "fsname", .has_arg = required_argument },
2495 { .val = 'h', .name = "help", .has_arg = no_argument },
2496 { .val = 'n', .name = "name", .has_arg = required_argument },
2497 { .val = 'r', .name = "rsh", .has_arg = required_argument },
2501 si = snapshot_init(argc, argv, long_opts, "F:hn:r:",
2502 snapshot_mount_usage, LOCK_EX, &rc);
2506 if (!si->si_ssname) {
2508 "Miss the snapshot name to be mounted\n");
2509 snapshot_mount_usage();
2514 rc = snapshot_mount(si);
2516 SNAPSHOT_ADD_LOG(si, "Can't mount snapshot %s: %d\n",
2519 SNAPSHOT_ADD_LOG(si, "The snapshot %s is mounted\n",
2527 static void snapshot_umount_usage(void)
2530 "Umount the specified snapshot.\n"
2532 "snapshot_umount <-F | --fsname fsname> [-h | --help] "
2533 "<-n | --name ssname> "
2534 "[-r | --rsh remote_shell]\n"
2536 "-F: the filesystem name.\n"
2537 "-h: for help information.\n"
2538 "-n: the snapshot's name.\n"
2539 "-r: the remote shell used for communication with remote "
2540 "target, the default value is 'ssh'.\n");
2543 static int __snapshot_umount(struct snapshot_instance *si,
2544 struct list_head *head)
2546 struct snapshot_target *st;
2550 list_for_each_entry(st, head, st_list) {
2557 SNAPSHOT_ADD_LOG(si, "Can't fork for umount snapshot "
2558 "%s on target (%s(%s):%x:%d): %s\n",
2559 si->si_ssname, st->st_host,
2560 st->st_fhost ? st->st_fhost : "none",
2561 st->st_role, st->st_index,
2568 char cmd[MAX_BUF_SIZE];
2571 rc = target_is_mounted(si, st, si->si_ssname);
2578 memset(cmd, 0, sizeof(cmd));
2579 snprintf(cmd, sizeof(cmd) - 1,
2580 DRSH" 'umount "DSSNAME"' 2>/dev/null",
2581 PRSH(si, st, foreign), PSSNAME(si, st));
2582 rc = snapshot_exec(cmd);
2583 if (rc && !foreign && st->st_fhost) {
2591 /* parent continue to run more snapshot commands in parallel. */
2598 static int snapshot_umount(struct snapshot_instance *si)
2600 struct snapshot_target *st;
2607 rc = snapshot_general_check(si);
2610 "Can't umount the snapshot %s: %s\n",
2611 si->si_ssname, strerror(-rc));
2615 rc = __snapshot_umount(si, &si->si_mdts_list);
2616 rc1 = __snapshot_umount(si, &si->si_osts_list);
2617 failed = snapshot_wait(si, &rc2);
2619 list_for_each_entry(st, &si->si_mdts_list, st_list) {
2620 if (!st->st_ignored)
2624 list_for_each_entry(st, &si->si_osts_list, st_list) {
2625 if (!st->st_ignored)
2631 "The snapshot %s has not been mounted\n",
2638 "%d of %d pieces of the snapshot %s "
2639 "can't be umounted: %s\n",
2640 failed, needed, si->si_ssname, strerror(-rc2));
2642 return rc ? rc : (rc1 ? rc1 : rc2);
2645 int jt_snapshot_umount(int argc, char **argv)
2647 struct snapshot_instance *si;
2648 struct option long_opts[] = {
2649 { .val = 'F', .name = "fsname", .has_arg = required_argument },
2650 { .val = 'h', .name = "help", .has_arg = no_argument },
2651 { .val = 'n', .name = "name", .has_arg = required_argument },
2652 { .val = 'r', .name = "rsh", .has_arg = required_argument },
2656 si = snapshot_init(argc, argv, long_opts, "F:hn:r:",
2657 snapshot_umount_usage, LOCK_EX, &rc);
2661 if (!si->si_ssname) {
2663 "Miss the snapshot name to be umounted\n");
2664 snapshot_umount_usage();
2669 rc = snapshot_umount(si);
2671 SNAPSHOT_ADD_LOG(si, "Can't umount snapshot %s: %d\n",
2674 SNAPSHOT_ADD_LOG(si, "the snapshot %s have been umounted\n",