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 uint64_t fe_del_dir_cnt;
34 uint64_t fe_put_back_cnt;
35 fits_dirent_t *fe_dirent_chain;
36 };
37
38 struct fits_file {
39 fits_t *ff_fits;
40 uint64_t ff_len;
41 uint64_t ff_last_byte;
42 uint64_t ff_ino;
43 fits_path_t *ff_path;
44 fits_dirent_t *ff_dirent;
45 };
46
47 static int fits_dirent_del_pass2(void *fits_enump, char *name, uint64_t ino);
48 static int fits_dirent_mod_pass2(void *fits_enump, char *name,
49 uint64_t ino_old, uint64_t ino_new);
50 static int fits_dirent_unmod_pass2(void *fits_enump, char *name, uint64_t ino);
51 static int fits_dir_del_pass2(fits_t *f, uint64_t ino);
52 static int fits_add_pass2(fits_t *f, uint64_t ino);
53 static int fits_mod_pass2(fits_t *f, uint64_t ino);
54
55 fits_ops_t _ops = {
56 .fits_dirent_del = fits_dirent_del_pass2,
57 .fits_dirent_mod = fits_dirent_mod_pass2,
58 .fits_dirent_unmod = fits_dirent_unmod_pass2,
59 .fits_file_add = fits_add_pass2,
60 .fits_file_mod = fits_mod_pass2,
61 .fits_dir_add = fits_add_pass2,
62 .fits_dir_del = fits_dir_del_pass2,
63 .fits_dir_mod = fits_mod_pass2
64 };
65
66 int
67 fits_start2(fits_t *f, fits_ops_t **ops)
68 {
69 f->f_pass = PASS_UNLINK;
70 *ops = &_ops;
71
72 return (0);
73 }
74
75 int
76 fits_abort(fits_t *f)
77 {
78 fits_send_fini(f);
79
80 return (0);
81 }
82
83 int
84 fits_end(fits_t *f)
85 {
86 int ret;
87 int ret2;
88
89 fits_send_end(f);
90
91 ret = fits_assert_count_empty(&f->f_link_add_cnt);
92 ret += fits_assert_count_empty(&f->f_del_dir_cnt);
93 ret += fits_assert_count_empty(&f->f_put_back_cnt);
94
95 printf("!fits: fits_end calling fits_abort\n");
96 ret2 = fits_abort(f);
97
98 return (ret ? ret : ret2);
99 }
100
101 static int
102 dir_del(fits_t *f, uint64_t ino, uint64_t removed_entries)
103 {
104 fits_info_t si;
105 uint64_t parent;
106 int ret;
107 int exists;
108 uint64_t new_count;
109 uint64_t left;
110
111 while (1) {
112 ret = fits_get_info(f, ino, FITS_OLD, &si, FI_ATTR_GEN |
113 FI_ATTR_PARENT | FI_ATTR_NENTRIES);
114 if (ret)
115 return (ret);
116
117 if (si.si_parent > f->f_current_ino)
118 return (0);
119
120 if (removed_entries + 2 < si.si_nentries) /* '.' and '..' */
121 return (0);
122
123 fits_free_count(&f->f_del_dir_cnt, ino);
124 ret = fits_send_rmdir(f, NULL, ino);
125 if (ret)
126 return (ret);
127
128 parent = si.si_parent;
129 exists = fits_get_info(f, parent, FITS_NEW, &si,
130 FI_ATTR_MODE);
131 if (exists && exists != ENOENT)
132 return (exists);
133
134 ret = fits_get_count(&f->f_put_back_cnt, parent, &left, NULL);
135 if (ret && ret != ENOENT)
136 return (ret);
137 if (left > 0) {
138 ret = fits_add_count(&f->f_put_back_cnt, parent, -1, 0,
139 &left, NULL);
140 if (ret)
141 return (ret);
142 }
143 if ((int64_t)left < 0)
144 return (EINVAL);
145
146 if (exists == 0 && S_ISDIR(si.si_mode)) {
147 char *name;
148 uint64_t new_ino;
149 fits_info_t si_new;
150 int new;
151
152 new = fits_get_info(f, ino, FITS_NEW, &si_new,
153 FI_ATTR_MODE);
154 if (new && new != ENOENT)
155 return (new);
156 ret = fits_find_entry(f, parent, ino, FITS_OLD, &name);
157 if (ret)
158 return (ret);
159
160 ret = fits_lookup_entry(f, parent, name,
161 FITS_NEW, &new_ino);
162 if (ret && ret != ENOENT) {
163 fits_free_name(name);
164 return (ret);
165 }
166 if (ret == 0 && new_ino != ino) {
167 fits_dirent_t dirent = {
168 .fd_name = name,
169 .fd_parent_ino = parent
170 };
171
172 ret = fits_send_rename_from_tempname(f, &dirent,
173 ino, new_ino);
174 if (ret) {
175 fits_free_name(name);
176 return (ret);
177 }
178 } else if (ret == 0 && new_ino == ino &&
179 !S_ISDIR(si_new.si_mode)) {
180 fits_dirent_t dirent = {
181 .fd_name = name,
182 .fd_parent_ino = parent
183 };
184
185 ret = fits_dirent_add_file(f, &dirent, ino,
186 si_new.si_mode, 0);
187 if (ret) {
188 fits_free_name(name);
189 return (ret);
190 }
191 }
192 fits_free_name(name);
193 }
194
195 if (left == 0) {
196 fits_free_count(&f->f_put_back_cnt, parent);
197 if (exists == 0 && S_ISDIR(si.si_mode)) {
198 ret = fits_send_mtime_update(f, NULL, parent);
199 if (ret)
200 return (ret);
201 }
202 }
203
204 if (exists == 0 && S_ISDIR(si.si_mode))
205 return (0);
206
207 /* propagate deletion */
208 ret = fits_add_count(&f->f_del_dir_cnt, parent, 1, 0,
209 &new_count, NULL);
210 if (ret)
211 return (ret);
212
213 ino = parent;
214 removed_entries = new_count;
215 }
216 }
217
218 static int
219 enum_dir(fits_t *f, uint64_t ino, uint64_t *pput_back_cnt,
220 uint64_t *pdel_dir_cnt)
221 {
222 int ret;
223 struct fits_enum fe = {
224 .fe_fits = f,
225 .fe_parent_ino = ino,
226 .fe_del_dir_cnt = 0,
227 .fe_put_back_cnt = 0
228 };
229
230 ret = fits_dir_contents(f, ino, &fe);
231 if (ret)
232 return (ret);
233
234 if (pput_back_cnt)
235 *pput_back_cnt = fe.fe_put_back_cnt;
236 if (pdel_dir_cnt)
237 *pdel_dir_cnt = fe.fe_del_dir_cnt;
238
239 if (fe.fe_put_back_cnt) {
240 ret = fits_add_count(&f->f_put_back_cnt, ino,
241 fe.fe_put_back_cnt, 0, NULL, NULL);
242 if (ret)
243 return (ret);
244 }
245
246 return (0);
247 }
248
249 static int
250 dirent_del_file(struct fits_enum *fe, fits_dirent_t *dirent,
251 uint64_t ino, uint64_t remains)
252 {
253 int ret;
254
255 ret = fits_send_unlink(fe->fe_fits, dirent, ino);
256 if (ret)
257 return (ret);
258
259 if (remains != FITS_NO_INO) {
260 ret = fits_send_rename_from_tempname(fe->fe_fits, dirent,
261 ino, remains);
262 if (ret)
263 return (ret);
264 }
265
266 fe->fe_del_dir_cnt++;
267
268 return (0);
269 }
270
271 static int
272 dirent_del_dir(struct fits_enum *fe, fits_dirent_t *dirent, uint64_t ino,
273 uint64_t remains)
274 {
275 int ret;
276 fits_info_t si;
277 fits_info_t si_old;
278 int new;
279 int old;
280 fits_t *f = fe->fe_fits;
281
282 new = fits_get_info(f, ino, FITS_NEW, &si, FI_ATTR_GEN |
283 FI_ATTR_MODE);
284 if (new && new != ENOENT)
285 return (new);
286 old = fits_get_info(f, ino, FITS_OLD, &si_old,
287 FI_ATTR_NENTRIES | FI_ATTR_GEN |
288 FI_ATTR_PARENT);
289 if (old)
290 return (old);
291
292 /* new == 0 means the dir was renamed, which happened during pass 1 */
293 if (new == ENOENT ||
294 (si.si_gen != si_old.si_gen && !S_ISDIR(si.si_mode))) {
295 uint64_t cnt;
296
297 if (ino > f->f_current_ino) {
298 ++fe->fe_put_back_cnt;
299 return (0);
300 }
301
302 ret = fits_get_count(&f->f_del_dir_cnt, ino, &cnt,
303 NULL);
304 if (ret && ret != ENOENT)
305 return (ret);
306 /* 2 for '.' and '..' */
307 if (cnt + 2 < si_old.si_nentries) {
308 ++fe->fe_put_back_cnt;
309 return (0);
310 }
311
312 fits_free_count(&f->f_del_dir_cnt, ino);
313 ret = fits_send_rmdir(f, dirent, ino);
314 if (ret)
315 return (ret);
316 }
317 if (remains != FITS_NO_INO) {
318 ret = fits_send_rename_from_tempname(f, dirent, ino, remains);
319 if (ret)
320 return (ret);
321 }
322 fe->fe_del_dir_cnt++;
323 if (new == 0 && si.si_gen != si_old.si_gen && !S_ISDIR(si.si_mode) &&
324 si_old.si_parent == dirent->fd_parent_ino) {
325 uint64_t parent = si_old.si_parent;
326 fits_info_t sip;
327 char *name = NULL;
328
329 ret = fits_get_info(f, parent, FITS_OLD, &sip,
330 FI_ATTR_MODE);
331 if (ret && ret != ENOENT)
332 return (ret);
333 if (ret == 0 && S_ISDIR(sip.si_mode)) {
334 uint64_t old_ino;
335
336 ret = fits_find_entry(f, parent, ino, FITS_OLD, &name);
337 if (ret)
338 return (ret);
339
340 ret = fits_lookup_entry(f, parent, name,
341 FITS_NEW, &old_ino);
342 if (ret && ret != ENOENT) {
343 fits_free_name(name);
344 return (ret);
345 }
346 if (ret == 0) {
347 ret = fits_dirent_add_file(f, dirent, ino,
348 si.si_mode, 0);
349 if (ret)
350 return (ret);
351 }
352 }
353 }
354
355 return (0);
356 }
357
358 static int
359 dirent_del(struct fits_enum *fe, fits_dirent_t *dirent,
360 uint64_t ino, uint64_t remains)
361 {
362 fits_info_t si;
363 int ret;
364
365 ret = fits_get_info(fe->fe_fits, ino, FITS_OLD, &si, FI_ATTR_MODE);
366 if (ret)
367 return (ret);
368
369 if (S_ISDIR(si.si_mode)) {
370 return (dirent_del_dir(fe, dirent, ino, remains));
371 } else {
372 return (dirent_del_file(fe, dirent, ino, remains));
373 }
374 }
375
376 static int
377 fits_dirent_del_pass2(void *fits_enump, char *name, uint64_t ino)
378 {
379 struct fits_enum *fe = fits_enump;
380 fits_dirent_t dirent = {
381 .fd_name = name,
382 .fd_parent_ino = fe->fe_parent_ino,
383 .fd_prev = fe->fe_dirent_chain,
384 };
385
386 return (dirent_del(fe, &dirent, ino, FITS_NO_INO));
387 }
388
389 static int
390 fits_dirent_mod_pass2(void *fits_enump, char *name,
391 uint64_t ino_old, uint64_t ino_new)
392 {
393 struct fits_enum *fe = fits_enump;
394 fits_dirent_t dirent = {
395 .fd_name = name,
396 .fd_parent_ino = fe->fe_parent_ino,
397 .fd_prev = fe->fe_dirent_chain,
398 };
399
400 return (dirent_del(fe, &dirent, ino_old, ino_new));
401 }
402
403 static int
404 fits_dirent_unmod_pass2(void *fits_enump, char *name, uint64_t ino)
405 {
406 struct fits_enum *fe = fits_enump;
407 fits_t *f = fe->fe_fits;
408 int ret;
409 uint64_t cnt;
410 fits_info_t si_old;
411 fits_info_t si_new;
412 fits_dirent_t dirent = {
413 .fd_name = name,
414 .fd_parent_ino = fe->fe_parent_ino,
415 .fd_prev = fe->fe_dirent_chain,
416 };
417
418 ret = fits_get_count(&f->f_link_add_cnt, ino, &cnt, NULL);
419 if (ret)
420 return (ret == ENOENT ? 0 : ret);
421
422 ret = fits_get_info(f, ino, FITS_OLD, &si_old, FI_ATTR_MODE);
423 if (ret)
424 return (ret);
425
426 if (S_ISDIR(si_old.si_mode)) {
427 return (dirent_del_dir(fe, &dirent, ino, 0));
428 }
429
430 ret = fits_get_info(f, ino, FITS_NEW, &si_new, FI_ATTR_MODE);
431 if (ret)
432 return (ret);
433 ret = fits_send_unlink(f, &dirent, ino);
434 if (ret)
435 return (ret);
436 if (S_ISDIR(si_new.si_mode)) {
437 fits_free_count(&f->f_link_add_cnt, ino);
438 ret = fits_send_rename_from_tempname(f, &dirent, ino, ino);
439 if (ret)
440 return (ret);
441 ret = fits_send_mtime_update(f, &dirent, ino);
442 } else {
443 ret = fits_dirent_add_file(f, &dirent, ino, si_new.si_mode, 0);
444 }
445
446 return (ret);
447 }
448
449 static int
450 fits_add_pass2(fits_t *f, uint64_t ino)
451 {
452 f->f_current_ino = ino;
453 f->f_current_path = NULL;
454
455 return (fits_send_mtime_update(f, NULL, ino));
456 }
457
458 static int
459 fits_dir_del_pass2(fits_t *f, uint64_t ino)
460 {
461 uint64_t put_back_cnt;
462 uint64_t del_dir_cnt;
463 uint64_t new_count;
464 int ret;
465
466 f->f_current_ino = ino;
467 f->f_current_path = NULL;
468
469 ret = enum_dir(f, ino, &put_back_cnt, &del_dir_cnt);
470 if (ret)
471 return (ret);
472
473 ret = fits_add_count(&f->f_del_dir_cnt, ino, del_dir_cnt,
474 0, &new_count, NULL);
475 if (ret)
476 return (ret);
477
478 if (put_back_cnt == 0) {
479 ret = dir_del(f, ino, new_count);
480 if (ret)
481 return (ret);
482 }
483
484 return (0);
485 }
486
487 static int
488 fits_mod_pass2(fits_t *f, uint64_t ino)
489 {
490 fits_info_t si_old;
491 fits_info_t si_new;
492 int ret;
493 uint64_t put_back_cnt = 0;
494
495 f->f_current_ino = ino;
496 f->f_current_path = NULL;
497
498 ret = fits_get_info(f, ino, FITS_NEW, &si_new, FI_ATTR_SIZE |
499 FI_ATTR_MODE | FI_ATTR_GEN | FI_ATTR_UID |
500 FI_ATTR_GID | FI_ATTR_SIZE);
501 if (ret)
502 return (ret);
503 ret = fits_get_info(f, ino, FITS_OLD, &si_old, FI_ATTR_GEN |
504 FI_ATTR_MODE | FI_ATTR_UID |
505 FI_ATTR_GID | FI_ATTR_SIZE);
506 if (ret)
507 return (ret);
508
509 if (!(S_ISDIR(si_old.si_mode) && S_ISDIR(si_new.si_mode)) &&
510 si_new.si_gen != si_old.si_gen) {
511 if (S_ISDIR(si_old.si_mode)) {
512 ret = fits_dir_del_pass2(f, ino);
513 if (ret)
514 return (ret);
515 }
516 return (fits_add_pass2(f, ino));
517 }
518
519 if (S_ISDIR(si_new.si_mode)) {
520 ret = enum_dir(f, ino, &put_back_cnt, NULL);
521 if (ret)
522 return (ret);
523 }
524
525 if (put_back_cnt)
526 return (0);
527
528 return (fits_send_mtime_update(f, NULL, ino));
529 }