1 /*
2    Copyright 2005-2017 Jakub Kruszona-Zawadzki, Gemius SA, 2013-2014 EditShare,
3    2013-2017 Skytechnology sp. z o.o..
4 
5    This file was part of MooseFS and is part of LizardFS.
6 
7    LizardFS is free software: you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation, version 3.
10 
11    LizardFS is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with LizardFS. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "common/platform.h"
21 
22 #include <errno.h>
23 #include <limits.h>
24 #include <signal.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/stat.h>
28 
29 #include "common/datapack.h"
30 #include "common/lambda_guard.h"
31 #include "common/moosefs_string.h"
32 #include "common/server_connection.h"
33 #include "protocol/cltoma.h"
34 #include "protocol/matocl.h"
35 #include "tools/tools_commands.h"
36 #include "tools/tools_common_functions.h"
37 
38 static int kDefaultTimeout = 60 * 1000;              // default timeout (60 seconds)
39 static int kInfiniteTimeout = 10 * 24 * 3600 * 1000; // simulate infinite timeout (10 days)
40 
41 
snapshot_usage()42 static void snapshot_usage() {
43 	fprintf(stderr,
44 	        "make snapshot (lazy copy)\n\nusage:\n lizardfs makesnapshot [-ofl] src [src ...] dst\n");
45 	fprintf(stderr, " -o,-f - allow to overwrite existing objects\n");
46 	fprintf(stderr, " -l - wait until snapshot will finish (otherwise there is 60s timeout)\n");
47 }
48 
make_snapshot(const char * dstdir,const char * dstbase,const char * srcname,uint32_t srcinode,uint8_t canoverwrite,int long_wait,uint8_t ignore_missing_src,int initial_batch_size)49 static int make_snapshot(const char *dstdir, const char *dstbase, const char *srcname,
50 	                 uint32_t srcinode, uint8_t canoverwrite, int long_wait, uint8_t ignore_missing_src, int initial_batch_size) {
51 	uint32_t nleng, dstinode, uid, gid;
52 	uint8_t status;
53 	uint32_t msgid = 0, job_id;
54 	int fd;
55 
56 	sigset_t set;
57 	sigemptyset(&set);
58 	sigaddset(&set, SIGINT);
59 	sigaddset(&set, SIGTERM);
60 	sigaddset(&set, SIGHUP);
61 	sigaddset(&set, SIGUSR1);
62 	sigprocmask(SIG_BLOCK, &set, NULL);
63 
64 	nleng = strlen(dstbase);
65 	if (nleng > 255) {
66 		printf("%s: name too long\n", dstbase);
67 		return -1;
68 	}
69 
70 	fd = open_master_conn(dstdir, &dstinode, NULL, true);
71 	if (fd < 0) {
72 		return -1;
73 	}
74 
75 	uid = getuid();
76 	gid = getgid();
77 
78 	printf("Creating snapshot: %s -> %s/%s ...\n", srcname, dstdir, dstbase);
79 	try {
80 		auto request = cltoma::requestTaskId::build(msgid);
81 		auto response = ServerConnection::sendAndReceive(fd, request,
82 				LIZ_MATOCL_REQUEST_TASK_ID,
83 				ServerConnection::ReceiveMode::kReceiveFirstNonNopMessage,
84 				long_wait ? kInfiniteTimeout : kDefaultTimeout);
85 		matocl::requestTaskId::deserialize(response, msgid, job_id);
86 
87 		std::thread signal_thread(std::bind(signalHandler, job_id));
88 
89 		/* destructor of LambdaGuard will send SIGUSR1 signal in order to
90 		 * return from signalHandler function and join thread */
91 		auto join_guard = makeLambdaGuard([&signal_thread]() {
92 			kill(getpid(), SIGUSR1);
93 			signal_thread.join();
94 		});
95 		request = cltoma::snapshot::build(msgid, job_id, srcinode, dstinode, MooseFsString<uint8_t>(dstbase),
96 		                                  uid, gid, canoverwrite, ignore_missing_src, initial_batch_size);
97 		response = ServerConnection::sendAndReceive(fd, request, LIZ_MATOCL_FUSE_SNAPSHOT,
98 				ServerConnection::ReceiveMode::kReceiveFirstNonNopMessage,
99 				long_wait ? kInfiniteTimeout : kDefaultTimeout);
100 		matocl::snapshot::deserialize(response, msgid, status);
101 
102 		close_master_conn(0);
103 
104 		if (status == LIZARDFS_STATUS_OK) {
105 			printf("Snapshot %s -> %s/%s completed\n", srcname, dstdir, dstbase);
106 			return 0;
107 		} else {
108 			printf("Snapshot %s -> %s/%s:\n returned error status %d: %s\n",
109 			       srcname, dstdir, dstbase, status, lizardfs_error_string(status));
110 			return -1;
111 		}
112 
113 	} catch (Exception &e) {
114 		fprintf(stderr, "%s\n", e.what());
115 		close_master_conn(1);
116 		return -1;
117 	}
118 	return 0;
119 }
120 
snapshot(const char * dstname,char * const * srcnames,uint32_t srcelements,uint8_t canowerwrite,int long_wait,uint8_t ignore_missing_src,int initial_batch_size)121 static int snapshot(const char *dstname, char *const *srcnames, uint32_t srcelements,
122 					uint8_t canowerwrite, int long_wait, uint8_t ignore_missing_src, int initial_batch_size) {
123 	char to[PATH_MAX + 1], base[PATH_MAX + 1], dir[PATH_MAX + 1];
124 	char src[PATH_MAX + 1];
125 	struct stat sst, dst;
126 	int status;
127 	uint32_t i, l;
128 
129 	if (stat(dstname, &dst) < 0) {  // dst does not exist
130 		if (errno != ENOENT) {
131 			printf("%s: stat error: %s\n", dstname, strerr(errno));
132 			return -1;
133 		}
134 		if (srcelements > 1) {
135 			printf("can snapshot multiple elements only into existing directory\n");
136 			return -1;
137 		}
138 		if (lstat(srcnames[0], &sst) < 0) {
139 			printf("%s: lstat error: %s\n", srcnames[0], strerr(errno));
140 			return -1;
141 		}
142 		if (bsd_dirname(dstname, dir) < 0) {
143 			printf("%s: dirname error\n", dstname);
144 			return -1;
145 		}
146 		if (stat(dir, &dst) < 0) {
147 			printf("%s: stat error: %s\n", dir, strerr(errno));
148 			return -1;
149 		}
150 		if (sst.st_dev != dst.st_dev) {
151 			printf("(%s,%s): both elements must be on the same device\n", dstname, srcnames[0]);
152 			return -1;
153 		}
154 		if (realpath(dir, to) == NULL) {
155 			printf("%s: realpath error on %s: %s\n", dir, to, strerr(errno));
156 			return -1;
157 		}
158 		if (bsd_basename(dstname, base) < 0) {
159 			printf("%s: basename error\n", dstname);
160 			return -1;
161 		}
162 		if (strlen(dstname) > 0 && dstname[strlen(dstname) - 1] == '/' && !S_ISDIR(sst.st_mode)) {
163 			printf("directory %s does not exist\n", dstname);
164 			return -1;
165 		}
166 		return make_snapshot(to, base, srcnames[0], sst.st_ino, canowerwrite, long_wait, ignore_missing_src, initial_batch_size);
167 	} else {  // dst exists
168 		if (realpath(dstname, to) == NULL) {
169 			printf("%s: realpath error on %s: %s\n", dstname, to, strerr(errno));
170 			return -1;
171 		}
172 		if (!S_ISDIR(dst.st_mode)) {  // dst id not a directory
173 			if (srcelements > 1) {
174 				printf("can snapshot multiple elements only into existing directory\n");
175 				return -1;
176 			}
177 			if (lstat(srcnames[0], &sst) < 0) {
178 				printf("%s: lstat error: %s\n", srcnames[0], strerr(errno));
179 				return -1;
180 			}
181 			if (sst.st_dev != dst.st_dev) {
182 				printf("(%s,%s): both elements must be on the same device\n", dstname, srcnames[0]);
183 				return -1;
184 			}
185 			memcpy(dir, to, PATH_MAX + 1);
186 			dirname_inplace(dir);
187 			if (bsd_basename(to, base) < 0) {
188 				printf("%s: basename error\n", to);
189 				return -1;
190 			}
191 			return make_snapshot(dir, base, srcnames[0], sst.st_ino, canowerwrite, long_wait, ignore_missing_src, initial_batch_size);
192 		} else {  // dst is a directory
193 			status = 0;
194 			for (i = 0; i < srcelements; i++) {
195 				if (lstat(srcnames[i], &sst) < 0) {
196 					printf("%s: lstat error: %s\n", srcnames[i], strerr(errno));
197 					status = -1;
198 					continue;
199 				}
200 				if (sst.st_dev != dst.st_dev) {
201 					printf("(%s,%s): both elements must be on the same device\n", dstname,
202 					       srcnames[i]);
203 					status = -1;
204 					continue;
205 				}
206 				if (!S_ISDIR(sst.st_mode)) {      // src is not a directory
207 					if (!S_ISLNK(sst.st_mode)) {  // src is not a symbolic link
208 						if (realpath(srcnames[i], src) == NULL) {
209 							printf("%s: realpath error on %s: %s\n", srcnames[i], src,
210 							       strerr(errno));
211 							status = -1;
212 							continue;
213 						}
214 						if (bsd_basename(src, base) < 0) {
215 							printf("%s: basename error\n", src);
216 							status = -1;
217 							continue;
218 						}
219 					} else {  // src is a symbolic link
220 						if (bsd_basename(srcnames[i], base) < 0) {
221 							printf("%s: basename error\n", srcnames[i]);
222 							status = -1;
223 							continue;
224 						}
225 					}
226 					if (make_snapshot(to, base, srcnames[i], sst.st_ino, canowerwrite, long_wait,
227 					                  ignore_missing_src, initial_batch_size) < 0) {
228 						status = -1;
229 					}
230 				} else {  // src is a directory
231 					l = strlen(srcnames[i]);
232 					if (l > 0 &&
233 					    srcnames[i][l - 1] !=
234 					        '/') {  // src is a directory and name has trailing slash
235 						if (realpath(srcnames[i], src) == NULL) {
236 							printf("%s: realpath error on %s: %s\n", srcnames[i], src,
237 							       strerr(errno));
238 							status = -1;
239 							continue;
240 						}
241 						if (bsd_basename(src, base) < 0) {
242 							printf("%s: basename error\n", src);
243 							status = -1;
244 							continue;
245 						}
246 						if (make_snapshot(to, base, srcnames[i], sst.st_ino, canowerwrite,
247 						                  long_wait, ignore_missing_src, initial_batch_size) < 0) {
248 							status = -1;
249 						}
250 					} else {  // src is a directory and name has not trailing slash
251 						memcpy(dir, to, PATH_MAX + 1);
252 						dirname_inplace(dir);
253 						if (bsd_basename(to, base) < 0) {
254 							printf("%s: basename error\n", to);
255 							status = -1;
256 							continue;
257 						}
258 						if (make_snapshot(dir, base, srcnames[i], sst.st_ino, canowerwrite,
259 						                  long_wait, ignore_missing_src, initial_batch_size) < 0) {
260 							status = -1;
261 						}
262 					}
263 				}
264 			}
265 			return status;
266 		}
267 	}
268 }
269 
snapshot_run(int argc,char ** argv)270 int snapshot_run(int argc, char **argv) {
271 	int ch;
272 	int oflag = 0;
273 	int lflag = 0;
274 	uint8_t ignore_missing_src = 0;
275 	int initial_batch_size = 0;
276 
277 	while ((ch = getopt(argc, argv, "folis:")) != -1) {
278 		switch (ch) {
279 		case 'f':
280 		case 'o':
281 			oflag = 1;
282 			break;
283 		case 'l':
284 			lflag = 1;
285 			break;
286 		case 'i':
287 			ignore_missing_src = 1;
288 			break;
289 		case 's':
290 			initial_batch_size = std::stoi(optarg);
291 			break;
292 		}
293 	}
294 	argc -= optind;
295 	argv += optind;
296 	if (argc < 2) {
297 		snapshot_usage();
298 		return 1;
299 	}
300 	return snapshot(argv[argc - 1], argv, argc - 1, oflag, lflag, ignore_missing_src, initial_batch_size);
301 }
302