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