1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2012 STRATO AG. All rights reserved.
23 */
24 #include <sys/zfs_context.h>
25 #include <sys/errno.h>
26 #include <sys/stat.h>
27 #include <sys/far.h>
28 #include <sys/far_impl.h>
29
30 struct far_enum {
31 far_t *fe_far;
32 uint64_t fe_parent_ino;
33 far_dirent_t *fe_dirent_chain;
34 };
35
36 struct far_file {
37 far_t *ff_far;
38 uint64_t ff_len;
39 uint64_t ff_last_byte;
40 uint64_t ff_ino;
41 far_path_t *ff_path;
42 far_dirent_t *ff_dirent;
43 };
44
45 static int far_file_data_pass1(void *far_filep, void *data, uint64_t off,
46 uint64_t len);
47 static int far_dirent_add_pass1(void *far_enump, char *name, uint64_t ino);
48 static int far_dirent_mod_pass1(void *far_enump, char *name,
49 uint64_t ino_old, uint64_t ino_new);
50 static int far_dir_add_pass1(far_t *f, uint64_t ino);
51 static int far_mod_pass1(far_t *f, uint64_t ino);
52
53 static far_ops_t _ops = {
54 .far_dir_add = far_dir_add_pass1,
55 .far_dir_mod = far_mod_pass1,
56 .far_dirent_add = far_dirent_add_pass1,
57 .far_dirent_mod = far_dirent_mod_pass1,
58 .far_file_mod = far_mod_pass1,
59 .far_file_data = far_file_data_pass1
60 };
61
62 static int far_file_add_genchange(far_t *f, uint64_t ino);
63
64 int
65 far_start(far_t *f, far_ops_t **ops)
66 {
67 int ret;
68
69 f->f_pass = PASS_LINK;
70 far_count_init(&f->f_link_add_cnt, "link_add_cnt");
71 far_count_init(&f->f_del_dir_cnt, "del_dir_cnt");
72 far_count_init(&f->f_put_back_cnt, "put_back_cnt");
73 far_send_init(f);
74
75 *ops = &_ops;
76
77 ret = far_send_start(f);
78 if (ret) {
79 far_abort(f);
80 return (ret);
81 }
82
83 return (0);
84 }
85
86 static int
87 enum_dir(far_t *f, uint64_t ino, far_dirent_t *chain)
88 {
89 struct far_enum fe = {
90 .fe_far = f,
91 .fe_parent_ino = ino,
92 .fe_dirent_chain = chain
93 };
94
95 return (far_dir_contents(f, ino, &fe));
96 }
97
98 static int
99 far_file_data_pass1(void *far_filep, void *data, uint64_t off, uint64_t len)
100 {
101 struct far_file *ff = far_filep;
102
103 if (off + len > ff->ff_len)
104 len = ff->ff_len - off;
105
106 ff->ff_last_byte = off + len;
107
108 return far_send_file_data(ff->ff_far, &ff->ff_path,
109 ff->ff_dirent, ff->ff_ino, off, len, data);
110 }
111
112 static int
113 dirent_add_dir(far_t *f, far_dirent_t *dirent, uint64_t ino, int exists)
114 {
115 far_info_t si_old;
116 far_info_t si_new;
117 int ret;
118
119 ret = far_get_info(f, ino, FAR_OLD, &si_old,
120 FI_ATTR_PARENT | FI_ATTR_GEN | FI_ATTR_MODE);
121 if (ret && ret != ENOENT)
122 return (ret);
123
124 if (ret == 0) {
125 ret = far_get_info(f, ino, FAR_NEW, &si_new,
126 FI_ATTR_GEN | FI_ATTR_MODE);
127 if (ret)
128 return (ret);
129
130 if (si_old.si_gen == si_new.si_gen ||
131 (S_ISDIR(si_new.si_mode) && S_ISDIR(si_old.si_mode)))
132 return far_send_rename(f, dirent, ino,
133 si_old.si_parent, exists);
134 return (0);
135 }
136
137 if (ino > f->f_current_ino)
138 return (0);
139
140 /* dir is new */
141 ret = far_send_mkdir(f, dirent, ino, exists);
142 if (ret)
143 return (ret);
144
145 return (enum_dir(f, ino, dirent));
146 }
147
148 int
149 far_dirent_add_file(far_t *f, far_dirent_t *dirent,
150 uint64_t ino, uint64_t mode, int exists)
151 {
152 far_info_t si_old;
153 far_info_t si_new;
154 int ret;
155 far_path_t *far_path;
156 uint64_t new_count;
157 uint64_t old_aux;
158
159 ret = far_get_info(f, ino, FAR_OLD, &si_old,
160 FI_ATTR_GEN | FI_ATTR_PARENT);
161 if (ret && ret != ENOENT)
162 return (ret);
163
164 if (ret == 0) {
165 ret = far_get_info(f, ino, FAR_NEW, &si_new,
166 FI_ATTR_GEN);
167 if (ret)
168 return (ret);
169
170 if (si_old.si_gen == si_new.si_gen)
171 return far_send_link(f, dirent, ino, si_old.si_parent,
172 FAR_OLD, exists);
173 }
174
175 /* file is new */
176 ret = far_add_count(&f->f_link_add_cnt, ino, 1, dirent->fd_parent_ino,
177 &new_count, &old_aux);
178 if (ret)
179 return (ret);
180
181 if (new_count == 1)
182 ret = far_send_create_file(f, dirent, ino,
183 exists, &far_path);
184 else
185 ret = far_send_link(f, dirent, ino, old_aux, FAR_NEW, exists);
186 if (ret)
187 return (ret);
188
189 ret = far_get_info(f, ino, FAR_NEW, &si_new,
190 FI_ATTR_SIZE | FI_ATTR_LINKS);
191 ASSERT(ret == 0);
192 if (new_count == 1 && S_ISREG(mode)) {
193 struct far_file ff;
194
195 ff.ff_ino = ino;
196 ff.ff_len = si_new.si_size;
197 ff.ff_far = f;
198 ff.ff_path = far_path;
199 ff.ff_dirent = dirent;
200 ff.ff_last_byte = 0;
201 ret = far_file_contents(f, ino, &ff);
202 far_path_free(ff.ff_path);
203 if (ret)
204 return (ret);
205 if (ff.ff_last_byte != si_new.si_size) {
206 /* sparse end */
207 ret = far_send_truncate(f, NULL, ino, si_new.si_size);
208 if (ret)
209 return (ret);
210 }
211 }
212 if (new_count == si_new.si_nlinks)
213 far_free_count(&f->f_link_add_cnt, ino);
214
215 return (0);
216 }
217
218 static int
219 dirent_add(far_t *f, far_dirent_t *dirent, uint64_t ino, int exists)
220 {
221 far_info_t si;
222 int ret;
223
224 ret = far_get_info(f, ino, FAR_NEW, &si, FI_ATTR_MODE);
225 if (ret)
226 return (ret);
227
228 if (S_ISDIR(si.si_mode)) {
229 return (dirent_add_dir(f, dirent, ino, exists));
230 } else {
231 return (far_dirent_add_file(f, dirent, ino, si.si_mode,
232 exists));
233 }
234 }
235
236 static int
237 far_dirent_add_pass1(void *far_enump, char *name, uint64_t ino)
238 {
239 struct far_enum *fe = far_enump;
240 far_dirent_t dirent = {
241 .fd_name = name,
242 .fd_parent_ino = fe->fe_parent_ino,
243 .fd_prev = fe->fe_dirent_chain,
244 };
245
246 return (dirent_add(fe->fe_far, &dirent, ino, 0));
247 }
248
249 static int
250 far_dirent_mod_pass1(void *far_enump, char *name,
251 uint64_t ino_old, uint64_t ino_new)
252 {
253 struct far_enum *fe = far_enump;
254 far_dirent_t dirent = {
255 .fd_name = name,
256 .fd_parent_ino = fe->fe_parent_ino,
257 .fd_prev = fe->fe_dirent_chain,
258 };
259
260 return (dirent_add(fe->fe_far, &dirent, ino_new, 1));
261 }
262
263 static int
264 far_file_add_genchange(far_t *f, uint64_t ino)
265 {
266 int ret;
267 char *name = NULL;
268
269 f->f_current_ino = ino;
270 f->f_current_path = NULL;
271
272 /*
273 * only called when generation has changed. TODO: move to own
274 * function
275 */
276 far_info_t si_old;
277 far_info_t si_new;
278 int same_name = 0;
279
280 ret = far_get_info(f, ino, FAR_OLD, &si_old, FI_ATTR_MODE |
281 FI_ATTR_LINKS | FI_ATTR_PARENT);
282 if (ret)
283 return (ret);
284 ret = far_get_info(f, ino, FAR_NEW, &si_new, FI_ATTR_MODE |
285 FI_ATTR_LINKS | FI_ATTR_PARENT);
286 if (ret)
287 return (ret);
288
289 if (si_old.si_nlinks > 1 && si_new.si_nlinks > 1)
290 return far_add_count(&f->f_link_add_cnt, ino, 0, 0, NULL,
291 NULL);
292
293 if (S_ISDIR(si_old.si_mode))
294 return (0);
295
296 far_which_t from;
297 far_which_t to;
298 uint64_t new_ino;
299 uint64_t parent = si_new.si_parent;
300
301 if (si_old.si_nlinks == 1) {
302 from = FAR_OLD;
303 to = FAR_NEW;
304 parent = si_old.si_parent;
305 } else if (si_old.si_nlinks > 1 && si_new.si_nlinks == 1) {
306 from = FAR_NEW;
307 to = FAR_OLD;
308 parent = si_new.si_parent;
309 } else {
310 return (EINVAL);
311 }
312
313 ret = far_find_entry(f, parent, ino, from, &name);
314 if (ret)
315 return (ret);
316
317 ret = far_lookup_entry(f, parent, name, to, &new_ino);
318 if (ret && ret != ENOENT)
319 goto out;
320 if (ret == 0 && new_ino == ino)
321 same_name = 1;
322
323 if ((si_old.si_nlinks == 1 || si_new.si_nlinks == 1) && !same_name) {
324 ret = 0;
325 goto out;
326 }
327
328 far_dirent_t dirent = {
329 .fd_parent_ino = parent,
330 .fd_name = name,
331 .fd_prev = NULL
332 };
333
334 ret = far_send_unlink(f, &dirent, ino);
335 if (ret)
336 goto out;
337 ret = far_dirent_add_file(f, &dirent, ino, si_new.si_mode, 0);
338
339 out:
340 far_free_name(name);
341 return (ret);
342 }
343
344 static int
345 far_dir_add_pass1(far_t *f, uint64_t ino)
346 {
347 int ret;
348 uint64_t parent;
349 uint64_t first_parent = FAR_NO_INO;
350 far_info_t si;
351 far_dirent_t dirent;
352 int same_name = 0;
353 char *name = NULL;
354
355 f->f_current_ino = ino;
356 f->f_current_path = NULL;
357
358 parent = ino;
359 while (1) {
360 /* the new parent must exist, otherwise the fs is wrong */
361 ret = far_get_info(f, parent, FAR_NEW, &si,
362 FI_ATTR_PARENT);
363 if (ret)
364 return (ret);
365 if (first_parent == FAR_NO_INO)
366 first_parent = si.si_parent;
367
368 ret = far_get_info(f, parent, FAR_OLD, &si, 0);
369 if (ret && ret != ENOENT)
370 return (ret);
371 if (ret != ENOENT)
372 break;
373
374 /*
375 * this check is only needed for a full send, on all
376 * incrementals the parent already exists and it breaks out
377 * above
378 */
379 if (parent == si.si_parent) {
380 first_parent = FAR_NO_INO;
381 break;
382 }
383 parent = si.si_parent;
384
385 if (parent > ino)
386 return (0);
387 }
388
389 /*
390 * check for same-name
391 */
392 if (first_parent != FAR_NO_INO) {
393 ret = far_get_info(f, first_parent, FAR_OLD, &si,
394 FI_ATTR_MODE);
395 if (ret && ret != ENOENT)
396 return (ret);
397 if (ret == 0 && S_ISDIR(si.si_mode)) {
398 uint64_t old_ino;
399
400 ret = far_find_entry(f, first_parent, ino,
401 FAR_NEW, &name);
402 if (ret)
403 return (ret);
404
405 ret = far_lookup_entry(f, first_parent, name,
406 FAR_OLD, &old_ino);
407 if (ret && ret != ENOENT) {
408 goto out;
409 }
410 if (ret == 0) {
411 same_name = 1;
412 dirent.fd_name = name;
413 dirent.fd_parent_ino = first_parent;
414 dirent.fd_prev = NULL;
415 if (old_ino == ino) {
416 ret = far_add_count(&f->f_link_add_cnt,
417 ino, 0, 0, NULL, NULL);
418 if (ret)
419 goto out;
420 }
421 }
422 }
423 }
424 /* dir is new */
425 ret = far_send_mkdir(f, same_name ? &dirent : NULL, ino, same_name);
426 if (ret)
427 goto out;
428
429 ret = enum_dir(f, ino, NULL);
430
431 out:
432 far_free_name(name);
433 return (ret);
434 }
435
436 static int
437 far_mod_pass1(far_t *f, uint64_t ino)
438 {
439 far_info_t si_old;
440 far_info_t si_new;
441 int ret;
442
443 f->f_current_ino = ino;
444 f->f_current_path = NULL;
445
446 ret = far_get_info(f, ino, FAR_NEW, &si_new, FI_ATTR_SIZE |
447 FI_ATTR_MODE | FI_ATTR_GEN | FI_ATTR_UID |
448 FI_ATTR_GID | FI_ATTR_SIZE);
449 if (ret)
450 return (ret);
451 ret = far_get_info(f, ino, FAR_OLD, &si_old, FI_ATTR_GEN |
452 FI_ATTR_MODE | FI_ATTR_UID |
453 FI_ATTR_GID | FI_ATTR_SIZE);
454 if (ret)
455 return (ret);
456
457 if (!(S_ISDIR(si_old.si_mode) && S_ISDIR(si_new.si_mode)) &&
458 si_new.si_gen != si_old.si_gen) {
459 if (S_ISDIR(si_new.si_mode))
460 return (far_dir_add_pass1(f, ino));
461 else
462 return (far_file_add_genchange(f, ino));
463 }
464
465 if (S_ISDIR(si_new.si_mode)) {
466 ret = enum_dir(f, ino, NULL);
467 if (ret)
468 return (ret);
469 }
470
471 if (S_ISREG(si_new.si_mode)) {
472 struct far_file ff;
473 ff.ff_ino = ino;
474 ff.ff_len = si_new.si_size;
475 ff.ff_far = f;
476 ff.ff_path = NULL;
477 ff.ff_dirent = NULL;
478 ff.ff_last_byte = 0;
479
480 ret = far_file_contents(f, ino, &ff);
481 far_path_free(ff.ff_path);
482 if (ret)
483 return (ret);
484 if (si_new.si_size < si_old.si_size ||
485 (si_new.si_size != si_old.si_size &&
486 si_new.si_size != ff.ff_last_byte)) {
487 ret = far_send_truncate(f, NULL, ino, si_new.si_size);
488 if (ret)
489 return (ret);
490 }
491 }
492
493 if (si_old.si_uid != si_new.si_uid || si_old.si_gid != si_new.si_gid) {
494 ret = far_send_chown(f, NULL, ino, si_new.si_uid,
495 si_new.si_gid);
496 if (ret)
497 return (ret);
498 }
499 if (si_old.si_mode != si_new.si_mode) {
500 ret = far_send_chmod(f, NULL, ino, si_new.si_mode);
501 if (ret)
502 return (ret);
503 }
504
505 return (ret);
506 }