]>
Commit | Line | Data |
---|---|---|
c088e31d SF |
1 | /* |
2 | * Copyright (C) 2011-2017 Junjiro R. Okajima | |
3 | * | |
4 | * This program, aufs is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | */ | |
17 | ||
18 | /* | |
19 | * move-down, opposite of copy-up | |
20 | */ | |
21 | ||
22 | #include "aufs.h" | |
23 | ||
24 | struct au_mvd_args { | |
25 | struct { | |
26 | struct super_block *h_sb; | |
27 | struct dentry *h_parent; | |
28 | struct au_hinode *hdir; | |
29 | struct inode *h_dir, *h_inode; | |
30 | struct au_pin pin; | |
31 | } info[AUFS_MVDOWN_NARRAY]; | |
32 | ||
33 | struct aufs_mvdown mvdown; | |
34 | struct dentry *dentry, *parent; | |
35 | struct inode *inode, *dir; | |
36 | struct super_block *sb; | |
37 | aufs_bindex_t bopq, bwh, bfound; | |
38 | unsigned char rename_lock; | |
39 | }; | |
40 | ||
41 | #define mvd_errno mvdown.au_errno | |
42 | #define mvd_bsrc mvdown.stbr[AUFS_MVDOWN_UPPER].bindex | |
43 | #define mvd_src_brid mvdown.stbr[AUFS_MVDOWN_UPPER].brid | |
44 | #define mvd_bdst mvdown.stbr[AUFS_MVDOWN_LOWER].bindex | |
45 | #define mvd_dst_brid mvdown.stbr[AUFS_MVDOWN_LOWER].brid | |
46 | ||
47 | #define mvd_h_src_sb info[AUFS_MVDOWN_UPPER].h_sb | |
48 | #define mvd_h_src_parent info[AUFS_MVDOWN_UPPER].h_parent | |
49 | #define mvd_hdir_src info[AUFS_MVDOWN_UPPER].hdir | |
50 | #define mvd_h_src_dir info[AUFS_MVDOWN_UPPER].h_dir | |
51 | #define mvd_h_src_inode info[AUFS_MVDOWN_UPPER].h_inode | |
52 | #define mvd_pin_src info[AUFS_MVDOWN_UPPER].pin | |
53 | ||
54 | #define mvd_h_dst_sb info[AUFS_MVDOWN_LOWER].h_sb | |
55 | #define mvd_h_dst_parent info[AUFS_MVDOWN_LOWER].h_parent | |
56 | #define mvd_hdir_dst info[AUFS_MVDOWN_LOWER].hdir | |
57 | #define mvd_h_dst_dir info[AUFS_MVDOWN_LOWER].h_dir | |
58 | #define mvd_h_dst_inode info[AUFS_MVDOWN_LOWER].h_inode | |
59 | #define mvd_pin_dst info[AUFS_MVDOWN_LOWER].pin | |
60 | ||
61 | #define AU_MVD_PR(flag, ...) do { \ | |
62 | if (flag) \ | |
63 | pr_err(__VA_ARGS__); \ | |
64 | } while (0) | |
65 | ||
66 | static int find_lower_writable(struct au_mvd_args *a) | |
67 | { | |
68 | struct super_block *sb; | |
69 | aufs_bindex_t bindex, bbot; | |
70 | struct au_branch *br; | |
71 | ||
72 | sb = a->sb; | |
73 | bindex = a->mvd_bsrc; | |
74 | bbot = au_sbbot(sb); | |
75 | if (a->mvdown.flags & AUFS_MVDOWN_FHSM_LOWER) | |
76 | for (bindex++; bindex <= bbot; bindex++) { | |
77 | br = au_sbr(sb, bindex); | |
78 | if (au_br_fhsm(br->br_perm) | |
79 | && !sb_rdonly(au_br_sb(br))) | |
80 | return bindex; | |
81 | } | |
82 | else if (!(a->mvdown.flags & AUFS_MVDOWN_ROLOWER)) | |
83 | for (bindex++; bindex <= bbot; bindex++) { | |
84 | br = au_sbr(sb, bindex); | |
85 | if (!au_br_rdonly(br)) | |
86 | return bindex; | |
87 | } | |
88 | else | |
89 | for (bindex++; bindex <= bbot; bindex++) { | |
90 | br = au_sbr(sb, bindex); | |
91 | if (!sb_rdonly(au_br_sb(br))) { | |
92 | if (au_br_rdonly(br)) | |
93 | a->mvdown.flags | |
94 | |= AUFS_MVDOWN_ROLOWER_R; | |
95 | return bindex; | |
96 | } | |
97 | } | |
98 | ||
99 | return -1; | |
100 | } | |
101 | ||
102 | /* make the parent dir on bdst */ | |
103 | static int au_do_mkdir(const unsigned char dmsg, struct au_mvd_args *a) | |
104 | { | |
105 | int err; | |
106 | ||
107 | err = 0; | |
108 | a->mvd_hdir_src = au_hi(a->dir, a->mvd_bsrc); | |
109 | a->mvd_hdir_dst = au_hi(a->dir, a->mvd_bdst); | |
110 | a->mvd_h_src_parent = au_h_dptr(a->parent, a->mvd_bsrc); | |
111 | a->mvd_h_dst_parent = NULL; | |
112 | if (au_dbbot(a->parent) >= a->mvd_bdst) | |
113 | a->mvd_h_dst_parent = au_h_dptr(a->parent, a->mvd_bdst); | |
114 | if (!a->mvd_h_dst_parent) { | |
115 | err = au_cpdown_dirs(a->dentry, a->mvd_bdst); | |
116 | if (unlikely(err)) { | |
117 | AU_MVD_PR(dmsg, "cpdown_dirs failed\n"); | |
118 | goto out; | |
119 | } | |
120 | a->mvd_h_dst_parent = au_h_dptr(a->parent, a->mvd_bdst); | |
121 | } | |
122 | ||
123 | out: | |
124 | AuTraceErr(err); | |
125 | return err; | |
126 | } | |
127 | ||
128 | /* lock them all */ | |
129 | static int au_do_lock(const unsigned char dmsg, struct au_mvd_args *a) | |
130 | { | |
131 | int err; | |
132 | struct dentry *h_trap; | |
133 | ||
134 | a->mvd_h_src_sb = au_sbr_sb(a->sb, a->mvd_bsrc); | |
135 | a->mvd_h_dst_sb = au_sbr_sb(a->sb, a->mvd_bdst); | |
136 | err = au_pin(&a->mvd_pin_dst, a->dentry, a->mvd_bdst, | |
137 | au_opt_udba(a->sb), | |
138 | AuPin_MNT_WRITE | AuPin_DI_LOCKED); | |
139 | AuTraceErr(err); | |
140 | if (unlikely(err)) { | |
141 | AU_MVD_PR(dmsg, "pin_dst failed\n"); | |
142 | goto out; | |
143 | } | |
144 | ||
145 | if (a->mvd_h_src_sb != a->mvd_h_dst_sb) { | |
146 | a->rename_lock = 0; | |
147 | au_pin_init(&a->mvd_pin_src, a->dentry, a->mvd_bsrc, | |
148 | AuLsc_DI_PARENT, AuLsc_I_PARENT3, | |
149 | au_opt_udba(a->sb), | |
150 | AuPin_MNT_WRITE | AuPin_DI_LOCKED); | |
151 | err = au_do_pin(&a->mvd_pin_src); | |
152 | AuTraceErr(err); | |
153 | a->mvd_h_src_dir = d_inode(a->mvd_h_src_parent); | |
154 | if (unlikely(err)) { | |
155 | AU_MVD_PR(dmsg, "pin_src failed\n"); | |
156 | goto out_dst; | |
157 | } | |
158 | goto out; /* success */ | |
159 | } | |
160 | ||
161 | a->rename_lock = 1; | |
162 | au_pin_hdir_unlock(&a->mvd_pin_dst); | |
163 | err = au_pin(&a->mvd_pin_src, a->dentry, a->mvd_bsrc, | |
164 | au_opt_udba(a->sb), | |
165 | AuPin_MNT_WRITE | AuPin_DI_LOCKED); | |
166 | AuTraceErr(err); | |
167 | a->mvd_h_src_dir = d_inode(a->mvd_h_src_parent); | |
168 | if (unlikely(err)) { | |
169 | AU_MVD_PR(dmsg, "pin_src failed\n"); | |
170 | au_pin_hdir_lock(&a->mvd_pin_dst); | |
171 | goto out_dst; | |
172 | } | |
173 | au_pin_hdir_unlock(&a->mvd_pin_src); | |
174 | h_trap = vfsub_lock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, | |
175 | a->mvd_h_dst_parent, a->mvd_hdir_dst); | |
176 | if (h_trap) { | |
177 | err = (h_trap != a->mvd_h_src_parent); | |
178 | if (err) | |
179 | err = (h_trap != a->mvd_h_dst_parent); | |
180 | } | |
181 | BUG_ON(err); /* it should never happen */ | |
182 | if (unlikely(a->mvd_h_src_dir != au_pinned_h_dir(&a->mvd_pin_src))) { | |
183 | err = -EBUSY; | |
184 | AuTraceErr(err); | |
185 | vfsub_unlock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, | |
186 | a->mvd_h_dst_parent, a->mvd_hdir_dst); | |
187 | au_pin_hdir_lock(&a->mvd_pin_src); | |
188 | au_unpin(&a->mvd_pin_src); | |
189 | au_pin_hdir_lock(&a->mvd_pin_dst); | |
190 | goto out_dst; | |
191 | } | |
192 | goto out; /* success */ | |
193 | ||
194 | out_dst: | |
195 | au_unpin(&a->mvd_pin_dst); | |
196 | out: | |
197 | AuTraceErr(err); | |
198 | return err; | |
199 | } | |
200 | ||
201 | static void au_do_unlock(const unsigned char dmsg, struct au_mvd_args *a) | |
202 | { | |
203 | if (!a->rename_lock) | |
204 | au_unpin(&a->mvd_pin_src); | |
205 | else { | |
206 | vfsub_unlock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, | |
207 | a->mvd_h_dst_parent, a->mvd_hdir_dst); | |
208 | au_pin_hdir_lock(&a->mvd_pin_src); | |
209 | au_unpin(&a->mvd_pin_src); | |
210 | au_pin_hdir_lock(&a->mvd_pin_dst); | |
211 | } | |
212 | au_unpin(&a->mvd_pin_dst); | |
213 | } | |
214 | ||
215 | /* copy-down the file */ | |
216 | static int au_do_cpdown(const unsigned char dmsg, struct au_mvd_args *a) | |
217 | { | |
218 | int err; | |
219 | struct au_cp_generic cpg = { | |
220 | .dentry = a->dentry, | |
221 | .bdst = a->mvd_bdst, | |
222 | .bsrc = a->mvd_bsrc, | |
223 | .len = -1, | |
224 | .pin = &a->mvd_pin_dst, | |
225 | .flags = AuCpup_DTIME | AuCpup_HOPEN | |
226 | }; | |
227 | ||
228 | AuDbg("b%d, b%d\n", cpg.bsrc, cpg.bdst); | |
229 | if (a->mvdown.flags & AUFS_MVDOWN_OWLOWER) | |
230 | au_fset_cpup(cpg.flags, OVERWRITE); | |
231 | if (a->mvdown.flags & AUFS_MVDOWN_ROLOWER) | |
232 | au_fset_cpup(cpg.flags, RWDST); | |
233 | err = au_sio_cpdown_simple(&cpg); | |
234 | if (unlikely(err)) | |
235 | AU_MVD_PR(dmsg, "cpdown failed\n"); | |
236 | ||
237 | AuTraceErr(err); | |
238 | return err; | |
239 | } | |
240 | ||
241 | /* | |
242 | * unlink the whiteout on bdst if exist which may be created by UDBA while we | |
243 | * were sleeping | |
244 | */ | |
245 | static int au_do_unlink_wh(const unsigned char dmsg, struct au_mvd_args *a) | |
246 | { | |
247 | int err; | |
248 | struct path h_path; | |
249 | struct au_branch *br; | |
250 | struct inode *delegated; | |
251 | ||
252 | br = au_sbr(a->sb, a->mvd_bdst); | |
253 | h_path.dentry = au_wh_lkup(a->mvd_h_dst_parent, &a->dentry->d_name, br); | |
254 | err = PTR_ERR(h_path.dentry); | |
255 | if (IS_ERR(h_path.dentry)) { | |
256 | AU_MVD_PR(dmsg, "wh_lkup failed\n"); | |
257 | goto out; | |
258 | } | |
259 | ||
260 | err = 0; | |
261 | if (d_is_positive(h_path.dentry)) { | |
262 | h_path.mnt = au_br_mnt(br); | |
263 | delegated = NULL; | |
264 | err = vfsub_unlink(d_inode(a->mvd_h_dst_parent), &h_path, | |
265 | &delegated, /*force*/0); | |
266 | if (unlikely(err == -EWOULDBLOCK)) { | |
267 | pr_warn("cannot retry for NFSv4 delegation" | |
268 | " for an internal unlink\n"); | |
269 | iput(delegated); | |
270 | } | |
271 | if (unlikely(err)) | |
272 | AU_MVD_PR(dmsg, "wh_unlink failed\n"); | |
273 | } | |
274 | dput(h_path.dentry); | |
275 | ||
276 | out: | |
277 | AuTraceErr(err); | |
278 | return err; | |
279 | } | |
280 | ||
281 | /* | |
282 | * unlink the topmost h_dentry | |
283 | */ | |
284 | static int au_do_unlink(const unsigned char dmsg, struct au_mvd_args *a) | |
285 | { | |
286 | int err; | |
287 | struct path h_path; | |
288 | struct inode *delegated; | |
289 | ||
290 | h_path.mnt = au_sbr_mnt(a->sb, a->mvd_bsrc); | |
291 | h_path.dentry = au_h_dptr(a->dentry, a->mvd_bsrc); | |
292 | delegated = NULL; | |
293 | err = vfsub_unlink(a->mvd_h_src_dir, &h_path, &delegated, /*force*/0); | |
294 | if (unlikely(err == -EWOULDBLOCK)) { | |
295 | pr_warn("cannot retry for NFSv4 delegation" | |
296 | " for an internal unlink\n"); | |
297 | iput(delegated); | |
298 | } | |
299 | if (unlikely(err)) | |
300 | AU_MVD_PR(dmsg, "unlink failed\n"); | |
301 | ||
302 | AuTraceErr(err); | |
303 | return err; | |
304 | } | |
305 | ||
306 | /* Since mvdown succeeded, we ignore an error of this function */ | |
307 | static void au_do_stfs(const unsigned char dmsg, struct au_mvd_args *a) | |
308 | { | |
309 | int err; | |
310 | struct au_branch *br; | |
311 | ||
312 | a->mvdown.flags |= AUFS_MVDOWN_STFS_FAILED; | |
313 | br = au_sbr(a->sb, a->mvd_bsrc); | |
314 | err = au_br_stfs(br, &a->mvdown.stbr[AUFS_MVDOWN_UPPER].stfs); | |
315 | if (!err) { | |
316 | br = au_sbr(a->sb, a->mvd_bdst); | |
317 | a->mvdown.stbr[AUFS_MVDOWN_LOWER].brid = br->br_id; | |
318 | err = au_br_stfs(br, &a->mvdown.stbr[AUFS_MVDOWN_LOWER].stfs); | |
319 | } | |
320 | if (!err) | |
321 | a->mvdown.flags &= ~AUFS_MVDOWN_STFS_FAILED; | |
322 | else | |
323 | AU_MVD_PR(dmsg, "statfs failed (%d), ignored\n", err); | |
324 | } | |
325 | ||
326 | /* | |
327 | * copy-down the file and unlink the bsrc file. | |
328 | * - unlink the bdst whout if exist | |
329 | * - copy-down the file (with whtmp name and rename) | |
330 | * - unlink the bsrc file | |
331 | */ | |
332 | static int au_do_mvdown(const unsigned char dmsg, struct au_mvd_args *a) | |
333 | { | |
334 | int err; | |
335 | ||
336 | err = au_do_mkdir(dmsg, a); | |
337 | if (!err) | |
338 | err = au_do_lock(dmsg, a); | |
339 | if (unlikely(err)) | |
340 | goto out; | |
341 | ||
342 | /* | |
343 | * do not revert the activities we made on bdst since they should be | |
344 | * harmless in aufs. | |
345 | */ | |
346 | ||
347 | err = au_do_cpdown(dmsg, a); | |
348 | if (!err) | |
349 | err = au_do_unlink_wh(dmsg, a); | |
350 | if (!err && !(a->mvdown.flags & AUFS_MVDOWN_KUPPER)) | |
351 | err = au_do_unlink(dmsg, a); | |
352 | if (unlikely(err)) | |
353 | goto out_unlock; | |
354 | ||
355 | AuDbg("%pd2, 0x%x, %d --> %d\n", | |
356 | a->dentry, a->mvdown.flags, a->mvd_bsrc, a->mvd_bdst); | |
357 | if (find_lower_writable(a) < 0) | |
358 | a->mvdown.flags |= AUFS_MVDOWN_BOTTOM; | |
359 | ||
360 | if (a->mvdown.flags & AUFS_MVDOWN_STFS) | |
361 | au_do_stfs(dmsg, a); | |
362 | ||
363 | /* maintain internal array */ | |
364 | if (!(a->mvdown.flags & AUFS_MVDOWN_KUPPER)) { | |
365 | au_set_h_dptr(a->dentry, a->mvd_bsrc, NULL); | |
366 | au_set_dbtop(a->dentry, a->mvd_bdst); | |
367 | au_set_h_iptr(a->inode, a->mvd_bsrc, NULL, /*flags*/0); | |
368 | au_set_ibtop(a->inode, a->mvd_bdst); | |
369 | } else { | |
370 | /* hide the lower */ | |
371 | au_set_h_dptr(a->dentry, a->mvd_bdst, NULL); | |
372 | au_set_dbbot(a->dentry, a->mvd_bsrc); | |
373 | au_set_h_iptr(a->inode, a->mvd_bdst, NULL, /*flags*/0); | |
374 | au_set_ibbot(a->inode, a->mvd_bsrc); | |
375 | } | |
376 | if (au_dbbot(a->dentry) < a->mvd_bdst) | |
377 | au_set_dbbot(a->dentry, a->mvd_bdst); | |
378 | if (au_ibbot(a->inode) < a->mvd_bdst) | |
379 | au_set_ibbot(a->inode, a->mvd_bdst); | |
380 | ||
381 | out_unlock: | |
382 | au_do_unlock(dmsg, a); | |
383 | out: | |
384 | AuTraceErr(err); | |
385 | return err; | |
386 | } | |
387 | ||
388 | /* ---------------------------------------------------------------------- */ | |
389 | ||
390 | /* make sure the file is idle */ | |
391 | static int au_mvd_args_busy(const unsigned char dmsg, struct au_mvd_args *a) | |
392 | { | |
393 | int err, plinked; | |
394 | ||
395 | err = 0; | |
396 | plinked = !!au_opt_test(au_mntflags(a->sb), PLINK); | |
397 | if (au_dbtop(a->dentry) == a->mvd_bsrc | |
398 | && au_dcount(a->dentry) == 1 | |
399 | && atomic_read(&a->inode->i_count) == 1 | |
400 | /* && a->mvd_h_src_inode->i_nlink == 1 */ | |
401 | && (!plinked || !au_plink_test(a->inode)) | |
402 | && a->inode->i_nlink == 1) | |
403 | goto out; | |
404 | ||
405 | err = -EBUSY; | |
406 | AU_MVD_PR(dmsg, | |
407 | "b%d, d{b%d, c%d?}, i{c%d?, l%u}, hi{l%u}, p{%d, %d}\n", | |
408 | a->mvd_bsrc, au_dbtop(a->dentry), au_dcount(a->dentry), | |
409 | atomic_read(&a->inode->i_count), a->inode->i_nlink, | |
410 | a->mvd_h_src_inode->i_nlink, | |
411 | plinked, plinked ? au_plink_test(a->inode) : 0); | |
412 | ||
413 | out: | |
414 | AuTraceErr(err); | |
415 | return err; | |
416 | } | |
417 | ||
418 | /* make sure the parent dir is fine */ | |
419 | static int au_mvd_args_parent(const unsigned char dmsg, | |
420 | struct au_mvd_args *a) | |
421 | { | |
422 | int err; | |
423 | aufs_bindex_t bindex; | |
424 | ||
425 | err = 0; | |
426 | if (unlikely(au_alive_dir(a->parent))) { | |
427 | err = -ENOENT; | |
428 | AU_MVD_PR(dmsg, "parent dir is dead\n"); | |
429 | goto out; | |
430 | } | |
431 | ||
432 | a->bopq = au_dbdiropq(a->parent); | |
433 | bindex = au_wbr_nonopq(a->dentry, a->mvd_bdst); | |
434 | AuDbg("b%d\n", bindex); | |
435 | if (unlikely((bindex >= 0 && bindex < a->mvd_bdst) | |
436 | || (a->bopq != -1 && a->bopq < a->mvd_bdst))) { | |
437 | err = -EINVAL; | |
438 | a->mvd_errno = EAU_MVDOWN_OPAQUE; | |
439 | AU_MVD_PR(dmsg, "ancestor is opaque b%d, b%d\n", | |
440 | a->bopq, a->mvd_bdst); | |
441 | } | |
442 | ||
443 | out: | |
444 | AuTraceErr(err); | |
445 | return err; | |
446 | } | |
447 | ||
448 | static int au_mvd_args_intermediate(const unsigned char dmsg, | |
449 | struct au_mvd_args *a) | |
450 | { | |
451 | int err; | |
452 | struct au_dinfo *dinfo, *tmp; | |
453 | ||
454 | /* lookup the next lower positive entry */ | |
455 | err = -ENOMEM; | |
456 | tmp = au_di_alloc(a->sb, AuLsc_DI_TMP); | |
457 | if (unlikely(!tmp)) | |
458 | goto out; | |
459 | ||
460 | a->bfound = -1; | |
461 | a->bwh = -1; | |
462 | dinfo = au_di(a->dentry); | |
463 | au_di_cp(tmp, dinfo); | |
464 | au_di_swap(tmp, dinfo); | |
465 | ||
466 | /* returns the number of positive dentries */ | |
467 | err = au_lkup_dentry(a->dentry, a->mvd_bsrc + 1, | |
468 | /* AuLkup_IGNORE_PERM */ 0); | |
469 | if (!err) | |
470 | a->bwh = au_dbwh(a->dentry); | |
471 | else if (err > 0) | |
472 | a->bfound = au_dbtop(a->dentry); | |
473 | ||
474 | au_di_swap(tmp, dinfo); | |
475 | au_rw_write_unlock(&tmp->di_rwsem); | |
476 | au_di_free(tmp); | |
477 | if (unlikely(err < 0)) | |
478 | AU_MVD_PR(dmsg, "failed look-up lower\n"); | |
479 | ||
480 | /* | |
481 | * here, we have these cases. | |
482 | * bfound == -1 | |
483 | * no positive dentry under bsrc. there are more sub-cases. | |
484 | * bwh < 0 | |
485 | * there no whiteout, we can safely move-down. | |
486 | * bwh <= bsrc | |
487 | * impossible | |
488 | * bsrc < bwh && bwh < bdst | |
489 | * there is a whiteout on RO branch. cannot proceed. | |
490 | * bwh == bdst | |
491 | * there is a whiteout on the RW target branch. it should | |
492 | * be removed. | |
493 | * bdst < bwh | |
494 | * there is a whiteout somewhere unrelated branch. | |
495 | * -1 < bfound && bfound <= bsrc | |
496 | * impossible. | |
497 | * bfound < bdst | |
498 | * found, but it is on RO branch between bsrc and bdst. cannot | |
499 | * proceed. | |
500 | * bfound == bdst | |
501 | * found, replace it if AUFS_MVDOWN_FORCE is set. otherwise return | |
502 | * error. | |
503 | * bdst < bfound | |
504 | * found, after we create the file on bdst, it will be hidden. | |
505 | */ | |
506 | ||
507 | AuDebugOn(a->bfound == -1 | |
508 | && a->bwh != -1 | |
509 | && a->bwh <= a->mvd_bsrc); | |
510 | AuDebugOn(-1 < a->bfound | |
511 | && a->bfound <= a->mvd_bsrc); | |
512 | ||
513 | err = -EINVAL; | |
514 | if (a->bfound == -1 | |
515 | && a->mvd_bsrc < a->bwh | |
516 | && a->bwh != -1 | |
517 | && a->bwh < a->mvd_bdst) { | |
518 | a->mvd_errno = EAU_MVDOWN_WHITEOUT; | |
519 | AU_MVD_PR(dmsg, "bsrc %d, bdst %d, bfound %d, bwh %d\n", | |
520 | a->mvd_bsrc, a->mvd_bdst, a->bfound, a->bwh); | |
521 | goto out; | |
522 | } else if (a->bfound != -1 && a->bfound < a->mvd_bdst) { | |
523 | a->mvd_errno = EAU_MVDOWN_UPPER; | |
524 | AU_MVD_PR(dmsg, "bdst %d, bfound %d\n", | |
525 | a->mvd_bdst, a->bfound); | |
526 | goto out; | |
527 | } | |
528 | ||
529 | err = 0; /* success */ | |
530 | ||
531 | out: | |
532 | AuTraceErr(err); | |
533 | return err; | |
534 | } | |
535 | ||
536 | static int au_mvd_args_exist(const unsigned char dmsg, struct au_mvd_args *a) | |
537 | { | |
538 | int err; | |
539 | ||
540 | err = 0; | |
541 | if (!(a->mvdown.flags & AUFS_MVDOWN_OWLOWER) | |
542 | && a->bfound == a->mvd_bdst) | |
543 | err = -EEXIST; | |
544 | AuTraceErr(err); | |
545 | return err; | |
546 | } | |
547 | ||
548 | static int au_mvd_args(const unsigned char dmsg, struct au_mvd_args *a) | |
549 | { | |
550 | int err; | |
551 | struct au_branch *br; | |
552 | ||
553 | err = -EISDIR; | |
554 | if (unlikely(S_ISDIR(a->inode->i_mode))) | |
555 | goto out; | |
556 | ||
557 | err = -EINVAL; | |
558 | if (!(a->mvdown.flags & AUFS_MVDOWN_BRID_UPPER)) | |
559 | a->mvd_bsrc = au_ibtop(a->inode); | |
560 | else { | |
561 | a->mvd_bsrc = au_br_index(a->sb, a->mvd_src_brid); | |
562 | if (unlikely(a->mvd_bsrc < 0 | |
563 | || (a->mvd_bsrc < au_dbtop(a->dentry) | |
564 | || au_dbbot(a->dentry) < a->mvd_bsrc | |
565 | || !au_h_dptr(a->dentry, a->mvd_bsrc)) | |
566 | || (a->mvd_bsrc < au_ibtop(a->inode) | |
567 | || au_ibbot(a->inode) < a->mvd_bsrc | |
568 | || !au_h_iptr(a->inode, a->mvd_bsrc)))) { | |
569 | a->mvd_errno = EAU_MVDOWN_NOUPPER; | |
570 | AU_MVD_PR(dmsg, "no upper\n"); | |
571 | goto out; | |
572 | } | |
573 | } | |
574 | if (unlikely(a->mvd_bsrc == au_sbbot(a->sb))) { | |
575 | a->mvd_errno = EAU_MVDOWN_BOTTOM; | |
576 | AU_MVD_PR(dmsg, "on the bottom\n"); | |
577 | goto out; | |
578 | } | |
579 | a->mvd_h_src_inode = au_h_iptr(a->inode, a->mvd_bsrc); | |
580 | br = au_sbr(a->sb, a->mvd_bsrc); | |
581 | err = au_br_rdonly(br); | |
582 | if (!(a->mvdown.flags & AUFS_MVDOWN_ROUPPER)) { | |
583 | if (unlikely(err)) | |
584 | goto out; | |
585 | } else if (!(vfsub_native_ro(a->mvd_h_src_inode) | |
586 | || IS_APPEND(a->mvd_h_src_inode))) { | |
587 | if (err) | |
588 | a->mvdown.flags |= AUFS_MVDOWN_ROUPPER_R; | |
589 | /* go on */ | |
590 | } else | |
591 | goto out; | |
592 | ||
593 | err = -EINVAL; | |
594 | if (!(a->mvdown.flags & AUFS_MVDOWN_BRID_LOWER)) { | |
595 | a->mvd_bdst = find_lower_writable(a); | |
596 | if (unlikely(a->mvd_bdst < 0)) { | |
597 | a->mvd_errno = EAU_MVDOWN_BOTTOM; | |
598 | AU_MVD_PR(dmsg, "no writable lower branch\n"); | |
599 | goto out; | |
600 | } | |
601 | } else { | |
602 | a->mvd_bdst = au_br_index(a->sb, a->mvd_dst_brid); | |
603 | if (unlikely(a->mvd_bdst < 0 | |
604 | || au_sbbot(a->sb) < a->mvd_bdst)) { | |
605 | a->mvd_errno = EAU_MVDOWN_NOLOWERBR; | |
606 | AU_MVD_PR(dmsg, "no lower brid\n"); | |
607 | goto out; | |
608 | } | |
609 | } | |
610 | ||
611 | err = au_mvd_args_busy(dmsg, a); | |
612 | if (!err) | |
613 | err = au_mvd_args_parent(dmsg, a); | |
614 | if (!err) | |
615 | err = au_mvd_args_intermediate(dmsg, a); | |
616 | if (!err) | |
617 | err = au_mvd_args_exist(dmsg, a); | |
618 | if (!err) | |
619 | AuDbg("b%d, b%d\n", a->mvd_bsrc, a->mvd_bdst); | |
620 | ||
621 | out: | |
622 | AuTraceErr(err); | |
623 | return err; | |
624 | } | |
625 | ||
626 | int au_mvdown(struct dentry *dentry, struct aufs_mvdown __user *uarg) | |
627 | { | |
628 | int err, e; | |
629 | unsigned char dmsg; | |
630 | struct au_mvd_args *args; | |
631 | struct inode *inode; | |
632 | ||
633 | inode = d_inode(dentry); | |
634 | err = -EPERM; | |
635 | if (unlikely(!capable(CAP_SYS_ADMIN))) | |
636 | goto out; | |
637 | ||
638 | err = -ENOMEM; | |
639 | args = kmalloc(sizeof(*args), GFP_NOFS); | |
640 | if (unlikely(!args)) | |
641 | goto out; | |
642 | ||
643 | err = copy_from_user(&args->mvdown, uarg, sizeof(args->mvdown)); | |
644 | if (!err) | |
645 | err = !access_ok(VERIFY_WRITE, uarg, sizeof(*uarg)); | |
646 | if (unlikely(err)) { | |
647 | err = -EFAULT; | |
648 | AuTraceErr(err); | |
649 | goto out_free; | |
650 | } | |
651 | AuDbg("flags 0x%x\n", args->mvdown.flags); | |
652 | args->mvdown.flags &= ~(AUFS_MVDOWN_ROLOWER_R | AUFS_MVDOWN_ROUPPER_R); | |
653 | args->mvdown.au_errno = 0; | |
654 | args->dentry = dentry; | |
655 | args->inode = inode; | |
656 | args->sb = dentry->d_sb; | |
657 | ||
658 | err = -ENOENT; | |
659 | dmsg = !!(args->mvdown.flags & AUFS_MVDOWN_DMSG); | |
660 | args->parent = dget_parent(dentry); | |
661 | args->dir = d_inode(args->parent); | |
662 | inode_lock_nested(args->dir, I_MUTEX_PARENT); | |
663 | dput(args->parent); | |
664 | if (unlikely(args->parent != dentry->d_parent)) { | |
665 | AU_MVD_PR(dmsg, "parent dir is moved\n"); | |
666 | goto out_dir; | |
667 | } | |
668 | ||
669 | inode_lock_nested(inode, I_MUTEX_CHILD); | |
670 | err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_NOPLMW); | |
671 | if (unlikely(err)) | |
672 | goto out_inode; | |
673 | ||
674 | di_write_lock_parent(args->parent); | |
675 | err = au_mvd_args(dmsg, args); | |
676 | if (unlikely(err)) | |
677 | goto out_parent; | |
678 | ||
679 | err = au_do_mvdown(dmsg, args); | |
680 | if (unlikely(err)) | |
681 | goto out_parent; | |
682 | ||
683 | au_cpup_attr_timesizes(args->dir); | |
684 | au_cpup_attr_timesizes(inode); | |
685 | if (!(args->mvdown.flags & AUFS_MVDOWN_KUPPER)) | |
686 | au_cpup_igen(inode, au_h_iptr(inode, args->mvd_bdst)); | |
687 | /* au_digen_dec(dentry); */ | |
688 | ||
689 | out_parent: | |
690 | di_write_unlock(args->parent); | |
691 | aufs_read_unlock(dentry, AuLock_DW); | |
692 | out_inode: | |
693 | inode_unlock(inode); | |
694 | out_dir: | |
695 | inode_unlock(args->dir); | |
696 | out_free: | |
697 | e = copy_to_user(uarg, &args->mvdown, sizeof(args->mvdown)); | |
698 | if (unlikely(e)) | |
699 | err = -EFAULT; | |
700 | kfree(args); | |
701 | out: | |
702 | AuTraceErr(err); | |
703 | return err; | |
704 | } |