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/fits.h>
28 #include <sys/fits_impl.h>
29
30 struct fits_enum {
31 fits_t *fe_fits;
32 uint64_t fe_parent_ino;
33 fits_dirent_t *fe_dirent_chain;
34 };
35
36 struct fits_file {
37 fits_t *ff_fits;
38 uint64_t ff_len;
39 uint64_t ff_last_byte;
40 uint64_t ff_ino;
41 fits_path_t *ff_path;
42 fits_dirent_t *ff_dirent;
43 };
44
45 static int fits_file_data_pass1(void *fits_filep, void *data, uint64_t off,
46 uint64_t len);
47 static int fits_dirent_add_pass1(void *fits_enump, char *name, uint64_t ino);
48 static int fits_dirent_mod_pass1(void *fits_enump, char *name,
49 uint64_t ino_old, uint64_t ino_new);
50 static int fits_dir_add_pass1(fits_t *f, uint64_t ino);
51 static int fits_mod_pass1(fits_t *f, uint64_t ino);
52
53 static fits_ops_t _ops = {
54 .fits_dir_add = fits_dir_add_pass1,
55 .fits_dir_mod = fits_mod_pass1,
56 .fits_dirent_add = fits_dirent_add_pass1,
57 .fits_dirent_mod = fits_dirent_mod_pass1,
58 .fits_file_mod = fits_mod_pass1,
59 .fits_file_data = fits_file_data_pass1
60 };
61
62 static int fits_file_add_genchange(fits_t *f, uint64_t ino);
63
64 int
65 fits_start(fits_t *f, fits_ops_t **ops)
66 {
67 int ret;
68
69 f->f_pass = PASS_LINK;
70 f->f_link_add_cnt.fc_head = NULL;
71 f->f_link_add_cnt.fc_name = "link_add_cnt";
72 f->f_del_dir_cnt.fc_head = NULL;
73 f->f_del_dir_cnt.fc_name = "del_dir_cnt";
74 f->f_put_back_cnt.fc_head = NULL;
75 f->f_put_back_cnt.fc_name = "put_back_cnt";
76 fits_send_init(f);
77
78 *ops = &_ops;
79
80 ret = fits_send_start(f);
81 if (ret) {
82 fits_abort(f);
83 return (ret);
84 }
85
86 return (0);
87 }
88
89 static int
90 enum_dir(fits_t *f, uint64_t ino, fits_dirent_t *chain)
91 {
92 struct fits_enum fe = {
93 .fe_fits = f,
94 .fe_parent_ino = ino,
95 .fe_dirent_chain = chain
96 };
97
98 return (fits_dir_contents(f, ino, &fe));
99 }
100
101 static int
102 fits_file_data_pass1(void *fits_filep, void *data, uint64_t off, uint64_t len)
103 {
104 struct fits_file *ff = fits_filep;
105
106 if (off + len > ff->ff_len)
107 len = ff->ff_len - off;
108
109 ff->ff_last_byte = off + len;
110
111 return fits_send_file_data(ff->ff_fits, &ff->ff_path,
112 ff->ff_dirent, ff->ff_ino, off, len, data);
113 }
114
115 static int
116 dirent_add_dir(fits_t *f, fits_dirent_t *dirent, uint64_t ino, int exists)
117 {
118 fits_info_t si_old;
119 fits_info_t si_new;
120 int ret;
121
122 ret = fits_get_info(f, ino, FITS_OLD, &si_old,
123 FI_ATTR_PARENT | FI_ATTR_GEN | FI_ATTR_MODE);
124 if (ret && ret != ENOENT)
125 return (ret);
126
127 if (ret == 0) {
128 ret = fits_get_info(f, ino, FITS_NEW, &si_new,
129 FI_ATTR_GEN | FI_ATTR_MODE);
130 if (ret)
131 return (ret);
132
133 if (si_old.si_gen == si_new.si_gen ||
134 (S_ISDIR(si_new.si_mode) && S_ISDIR(si_old.si_mode)))
135 return fits_send_rename(f, dirent, ino,
136 si_old.si_parent, exists);
137 return (0);
138 }
139
140 if (ino > f->f_current_ino)
141 return (0);
142
143 /* dir is new */
144 ret = fits_send_mkdir(f, dirent, ino, exists);
145 if (ret)
146 return (ret);
147
148 return (enum_dir(f, ino, dirent));
149 }
150
151 int
152 fits_dirent_add_file(fits_t *f, fits_dirent_t *dirent,
153 uint64_t ino, uint64_t mode, int exists)
154 {
155 fits_info_t si_old;
156 fits_info_t si_new;
157 int ret;
158 fits_path_t *fits_path;
159 uint64_t new_count;
160 uint64_t old_aux;
161
162 ret = fits_get_info(f, ino, FITS_OLD, &si_old,
163 FI_ATTR_GEN | FI_ATTR_PARENT);
164 if (ret && ret != ENOENT)
165 return (ret);
166
167 if (ret == 0) {
168 ret = fits_get_info(f, ino, FITS_NEW, &si_new,
169 FI_ATTR_GEN);
170 if (ret)
171 return (ret);
172
173 if (si_old.si_gen == si_new.si_gen)
174 return fits_send_link(f, dirent, ino, si_old.si_parent,
175 FITS_OLD, exists);
176 }
177
178 /* file is new */
179 ret = fits_add_count(&f->f_link_add_cnt, ino, 1, dirent->fd_parent_ino,
180 &new_count, &old_aux);
181 if (ret)
182 return (ret);
183
184 if (new_count == 1)
185 ret = fits_send_create_file(f, dirent, ino,
186 exists, &fits_path);
187 else
188 ret = fits_send_link(f, dirent, ino, old_aux, FITS_NEW, exists);
189 if (ret)
190 return (ret);
191
192 ret = fits_get_info(f, ino, FITS_NEW, &si_new,
193 FI_ATTR_SIZE | FI_ATTR_LINKS);
194 ASSERT(ret == 0);
195 if (new_count == 1 && S_ISREG(mode)) {
196 struct fits_file ff;
197
198 ff.ff_ino = ino;
199 ff.ff_len = si_new.si_size;
200 ff.ff_fits = f;
201 ff.ff_path = fits_path;
202 ff.ff_dirent = dirent;
203 ff.ff_last_byte = 0;
204 ret = fits_file_contents(f, ino, &ff);
205 fits_path_free(ff.ff_path);
206 if (ret)
207 return (ret);
208 if (ff.ff_last_byte != si_new.si_size) {
209 /* sparse end */
210 ret = fits_send_truncate(f, NULL, ino, si_new.si_size);
211 if (ret)
212 return (ret);
213 }
214 }
215 if (new_count == si_new.si_nlinks)
216 fits_free_count(&f->f_link_add_cnt, ino);
217
218 return (0);
219 }
220
221 static int
222 dirent_add(fits_t *f, fits_dirent_t *dirent, uint64_t ino, int exists)
223 {
224 fits_info_t si;
225 int ret;
226
227 ret = fits_get_info(f, ino, FITS_NEW, &si, FI_ATTR_MODE);
228 if (ret)
229 return (ret);
230
231 if (S_ISDIR(si.si_mode)) {
232 return (dirent_add_dir(f, dirent, ino, exists));
233 } else {
234 return (fits_dirent_add_file(f, dirent, ino, si.si_mode,
235 exists));
236 }
237 }
238
239 static int
240 fits_dirent_add_pass1(void *fits_enump, char *name, uint64_t ino)
241 {
242 struct fits_enum *fe = fits_enump;
243 fits_dirent_t dirent = {
244 .fd_name = name,
245 .fd_parent_ino = fe->fe_parent_ino,
246 .fd_prev = fe->fe_dirent_chain,
247 };
248
249 return (dirent_add(fe->fe_fits, &dirent, ino, 0));
250 }
251
252 static int
253 fits_dirent_mod_pass1(void *fits_enump, char *name,
254 uint64_t ino_old, uint64_t ino_new)
255 {
256 struct fits_enum *fe = fits_enump;
257 fits_dirent_t dirent = {
258 .fd_name = name,
259 .fd_parent_ino = fe->fe_parent_ino,
260 .fd_prev = fe->fe_dirent_chain,
261 };
262
263 return (dirent_add(fe->fe_fits, &dirent, ino_new, 1));
264 }
265
266 static int
267 fits_file_add_genchange(fits_t *f, uint64_t ino)
268 {
269 int ret;
270 char *name = NULL;
271
272 f->f_current_ino = ino;
273 f->f_current_path = NULL;
274
275 /*
276 * only called when generation has changed. TODO: move to own
277 * function
278 */
279 fits_info_t si_old;
280 fits_info_t si_new;
281 int same_name = 0;
282
283 ret = fits_get_info(f, ino, FITS_OLD, &si_old, FI_ATTR_MODE |
284 FI_ATTR_LINKS | FI_ATTR_PARENT);
285 if (ret)
286 return (ret);
287 ret = fits_get_info(f, ino, FITS_NEW, &si_new, FI_ATTR_MODE |
288 FI_ATTR_LINKS | FI_ATTR_PARENT);
289 if (ret)
290 return (ret);
291
292 if (si_old.si_nlinks > 1 && si_new.si_nlinks > 1)
293 return fits_add_count(&f->f_link_add_cnt, ino, 0, 0, NULL,
294 NULL);
295
296 if (S_ISDIR(si_old.si_mode))
297 return (0);
298
299 fits_which_t from;
300 fits_which_t to;
301 uint64_t new_ino;
302 uint64_t parent = si_new.si_parent;
303
304 if (si_old.si_nlinks == 1) {
305 from = FITS_OLD;
306 to = FITS_NEW;
307 parent = si_old.si_parent;
308 } else if (si_old.si_nlinks > 1 && si_new.si_nlinks == 1) {
309 from = FITS_NEW;
310 to = FITS_OLD;
311 parent = si_new.si_parent;
312 } else {
313 return (EINVAL);
314 }
315
316 ret = fits_find_entry(f, parent, ino, from, &name);
317 if (ret)
318 return (ret);
319
320 ret = fits_lookup_entry(f, parent, name, to, &new_ino);
321 if (ret && ret != ENOENT)
322 goto out;
323 if (ret == 0 && new_ino == ino)
324 same_name = 1;
325
326 if ((si_old.si_nlinks == 1 || si_new.si_nlinks == 1) && !same_name) {
327 ret = 0;
328 goto out;
329 }
330
331 fits_dirent_t dirent = {
332 .fd_parent_ino = parent,
333 .fd_name = name,
334 .fd_prev = NULL
335 };
336
337 ret = fits_send_unlink(f, &dirent, ino);
338 if (ret)
339 goto out;
340 ret = fits_dirent_add_file(f, &dirent, ino, si_new.si_mode, 0);
341
342 out:
343 fits_free_name(name);
344 return (ret);
345 }
346
347 static int
348 fits_dir_add_pass1(fits_t *f, uint64_t ino)
349 {
350 int ret;
351 uint64_t parent;
352 uint64_t first_parent = FITS_NO_INO;
353 fits_info_t si;
354 fits_dirent_t dirent;
355 int same_name = 0;
356 char *name = NULL;
357
358 f->f_current_ino = ino;
359 f->f_current_path = NULL;
360
361 parent = ino;
362 while (1) {
363 /* the new parent must exist, otherwise the fs is wrong */
364 ret = fits_get_info(f, parent, FITS_NEW, &si,
365 FI_ATTR_PARENT);
366 if (ret)
367 return (ret);
368 if (first_parent == FITS_NO_INO)
369 first_parent = si.si_parent;
370
371 ret = fits_get_info(f, parent, FITS_OLD, &si, 0);
372 if (ret && ret != ENOENT)
373 return (ret);
374 if (ret != ENOENT)
375 break;
376
377 /*
378 * this check is only needed for a full send, on all
379 * incrementals the parent already exists and it breaks out
380 * above
381 */
382 if (parent == si.si_parent) {
383 first_parent = FITS_NO_INO;
384 break;
385 }
386 parent = si.si_parent;
387
388 if (parent > ino)
389 return (0);
390 }
391
392 /*
393 * check for same-name
394 */
395 if (first_parent != FITS_NO_INO) {
396 ret = fits_get_info(f, first_parent, FITS_OLD, &si,
397 FI_ATTR_MODE);
398 if (ret && ret != ENOENT)
399 return (ret);
400 if (ret == 0 && S_ISDIR(si.si_mode)) {
401 uint64_t old_ino;
402
403 ret = fits_find_entry(f, first_parent, ino,
404 FITS_NEW, &name);
405 if (ret)
406 return (ret);
407
408 ret = fits_lookup_entry(f, first_parent, name,
409 FITS_OLD, &old_ino);
410 if (ret && ret != ENOENT) {
411 goto out;
412 }
413 if (ret == 0) {
414 same_name = 1;
415 dirent.fd_name = name;
416 dirent.fd_parent_ino = first_parent;
417 dirent.fd_prev = NULL;
418 if (old_ino == ino) {
419 ret = fits_add_count(&f->f_link_add_cnt,
420 ino, 0, 0, NULL, NULL);
421 if (ret)
422 goto out;
423 }
424 }
425 }
426 }
427 /* dir is new */
428 ret = fits_send_mkdir(f, same_name ? &dirent : NULL, ino, same_name);
429 if (ret)
430 goto out;
431
432 ret = enum_dir(f, ino, NULL);
433
434 out:
435 fits_free_name(name);
436 return (ret);
437 }
438
439 static int
440 fits_mod_pass1(fits_t *f, uint64_t ino)
441 {
442 fits_info_t si_old;
443 fits_info_t si_new;
444 int ret;
445
446 f->f_current_ino = ino;
447 f->f_current_path = NULL;
448
449 ret = fits_get_info(f, ino, FITS_NEW, &si_new, FI_ATTR_SIZE |
450 FI_ATTR_MODE | FI_ATTR_GEN | FI_ATTR_UID |
451 FI_ATTR_GID | FI_ATTR_SIZE);
452 if (ret)
453 return (ret);
454 ret = fits_get_info(f, ino, FITS_OLD, &si_old, FI_ATTR_GEN |
455 FI_ATTR_MODE | FI_ATTR_UID |
456 FI_ATTR_GID | FI_ATTR_SIZE);
457 if (ret)
458 return (ret);
459
460 if (!(S_ISDIR(si_old.si_mode) && S_ISDIR(si_new.si_mode)) &&
461 si_new.si_gen != si_old.si_gen) {
462 if (S_ISDIR(si_new.si_mode))
463 return (fits_dir_add_pass1(f, ino));
464 else
465 return (fits_file_add_genchange(f, ino));
466 }
467
468 if (S_ISDIR(si_new.si_mode)) {
469 ret = enum_dir(f, ino, NULL);
470 if (ret)
471 return (ret);
472 }
473
474 if (S_ISREG(si_new.si_mode)) {
475 struct fits_file ff;
476 ff.ff_ino = ino;
477 ff.ff_len = si_new.si_size;
478 ff.ff_fits = f;
479 ff.ff_path = NULL;
480 ff.ff_dirent = NULL;
481 ff.ff_last_byte = 0;
482
483 ret = fits_file_contents(f, ino, &ff);
484 fits_path_free(ff.ff_path);
485 if (ret)
486 return (ret);
487 if (si_new.si_size < si_old.si_size ||
488 (si_new.si_size != si_old.si_size &&
489 si_new.si_size != ff.ff_last_byte)) {
490 ret = fits_send_truncate(f, NULL, ino, si_new.si_size);
491 if (ret)
492 return (ret);
493 }
494 }
495
496 if (si_old.si_uid != si_new.si_uid || si_old.si_gid != si_new.si_gid) {
497 ret = fits_send_chown(f, NULL, ino, si_new.si_uid,
498 si_new.si_gid);
499 if (ret)
500 return (ret);
501 }
502 if (si_old.si_mode != si_new.si_mode) {
503 ret = fits_send_chmod(f, NULL, ino, si_new.si_mode);
504 if (ret)
505 return (ret);
506 }
507
508 return (ret);
509 }