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