2
* Copyright (C) 2005, 2006, 2007 Junjiro Okajima
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.
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.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, write to the Free Software
16
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
/* $Id: whout.c,v 1.13 2007/05/07 03:46:08 sfjro Exp $ */
22
#include <linux/namei.h>
23
#include <linux/random.h>
24
#include <linux/security.h>
27
#define WH_MASK S_IRUGO
29
/* If a directory contains this file, then it is opaque. We start with the
30
* .wh. flag so that it is blocked by lookup.
32
static struct qstr diropq_name = {
33
.name = AUFS_WH_DIROPQ,
34
.len = sizeof(AUFS_WH_DIROPQ) - 1
38
* generate whiteout name, which is NOT terminated by NULL.
39
* @name: original d_name.name
40
* @len: original d_name.len
42
* returns zero when succeeds, otherwise error.
43
* succeeded value as wh->name should be freed by au_free_whname().
45
int au_alloc_whname(const char *name, int len, struct qstr *wh)
49
DEBUG_ON(!name || !len || !wh);
51
if (unlikely(len > PATH_MAX - AUFS_WH_LEN))
54
wh->len = len + AUFS_WH_LEN;
55
wh->name = p = kmalloc(wh->len, GFP_KERNEL);
56
//if (LktrCond) {kfree(p); wh->name = p = NULL;}
58
memcpy(p, AUFS_WH_PFX, AUFS_WH_LEN);
59
memcpy(p + AUFS_WH_LEN, name, len);
66
void au_free_whname(struct qstr *wh)
68
DEBUG_ON(!wh || !wh->name);
70
#ifdef CONFIG_AUFS_DEBUG
75
/* ---------------------------------------------------------------------- */
78
* test if the @wh_name exists under @hidden_parent.
79
* @try_sio specifies the necessary of super-io.
81
int is_wh(struct dentry *hidden_parent, struct qstr *wh_name, int try_sio,
82
struct lkup_args *lkup)
85
struct dentry *wh_dentry;
86
struct inode *hidden_dir;
88
LKTRTrace("%.*s/%.*s, lkup{%p, %d}\n", DLNPair(hidden_parent),
89
wh_name->len, wh_name->name, lkup->nfsmnt, lkup->dlgt);
90
hidden_dir = hidden_parent->d_inode;
91
DEBUG_ON(!S_ISDIR(hidden_dir->i_mode));
92
IMustLock(hidden_dir);
95
wh_dentry = lkup_one(wh_name->name, hidden_parent,
98
wh_dentry = sio_lkup_one(wh_name->name, hidden_parent,
100
//if (LktrCond) {dput(wh_dentry); wh_dentry = ERR_PTR(-1);}
101
err = PTR_ERR(wh_dentry);
102
if (IS_ERR(wh_dentry))
106
if (!wh_dentry->d_inode)
107
goto out_wh; /* success */
110
if (S_ISREG(wh_dentry->d_inode->i_mode))
111
goto out_wh; /* success */
114
IOErr("%.*s Invalid whiteout entry type 0%o.\n",
115
DLNPair(wh_dentry), wh_dentry->d_inode->i_mode);
125
* test if the @hidden_dentry sets opaque or not.
127
int is_diropq(struct dentry *hidden_dentry, struct lkup_args *lkup)
130
struct inode *hidden_dir;
132
LKTRTrace("dentry %.*s\n", DLNPair(hidden_dentry));
133
hidden_dir = hidden_dentry->d_inode;
134
DEBUG_ON(!S_ISDIR(hidden_dir->i_mode));
135
IMustLock(hidden_dir);
137
err = is_wh(hidden_dentry, &diropq_name, /*try_sio*/1, lkup);
143
* returns a negative dentry whose name is unique and temporary.
145
struct dentry *lkup_whtmp(struct dentry *hidden_parent, struct qstr *prefix,
146
struct lkup_args *lkup)
149
struct dentry *dentry;
151
char defname[AUFS_WH_LEN * 2 + DNAME_INLINE_LEN_MIN + 1 + HEX_LEN + 1],
153
static unsigned char cnt;
155
LKTRTrace("hp %.*s, prefix %.*s\n",
156
DLNPair(hidden_parent), prefix->len, prefix->name);
157
DEBUG_ON(!hidden_parent->d_inode);
158
IMustLock(hidden_parent->d_inode);
161
len = sizeof(defname) - DNAME_INLINE_LEN_MIN + prefix->len - 1;
162
if (unlikely(prefix->len > DNAME_INLINE_LEN_MIN)) {
163
dentry = ERR_PTR(-ENAMETOOLONG);
164
if (unlikely(len >= PATH_MAX))
166
dentry = ERR_PTR(-ENOMEM);
167
name = kmalloc(len + 1, GFP_KERNEL);
168
//if (LktrCond) {kfree(name); name = NULL;}
173
// doubly whiteout-ed
174
memcpy(name, AUFS_WH_PFX AUFS_WH_PFX, AUFS_WH_LEN * 2);
175
p = name + AUFS_WH_LEN * 2;
176
memcpy(p, prefix->name, prefix->len);
179
DEBUG_ON(name + len + 1 - p <= HEX_LEN);
181
for (i = 0; i < 3; i++) {
182
sprintf(p, "%.*d", HEX_LEN, cnt++);
183
dentry = sio_lkup_one(name, hidden_parent, len, lkup);
184
//if (LktrCond) {dput(dentry); dentry = ERR_PTR(-1);}
185
if (unlikely(IS_ERR(dentry) || !dentry->d_inode))
189
//Warn("could not get random name\n");
190
dentry = ERR_PTR(-EEXIST);
191
Dbg("%.*s\n", len, name);
195
if (unlikely(name != defname))
204
* rename the @dentry of @bindex to the whiteouted temporary name.
206
int rename_whtmp(struct dentry *dentry, aufs_bindex_t bindex)
209
struct inode *hidden_dir;
210
struct dentry *hidden_dentry, *hidden_parent, *tmp_dentry;
211
struct super_block *sb;
212
struct lkup_args lkup;
214
LKTRTrace("%.*s, b%d\n", DLNPair(dentry), bindex);
215
hidden_dentry = au_h_dptr_i(dentry, bindex);
216
DEBUG_ON(!hidden_dentry || !hidden_dentry->d_inode);
217
hidden_parent = hidden_dentry->d_parent;
218
hidden_dir = hidden_parent->d_inode;
219
IMustLock(hidden_dir);
222
lkup.nfsmnt = au_nfsmnt(sb, bindex);
223
lkup.dlgt = need_dlgt(sb);
224
tmp_dentry = lkup_whtmp(hidden_parent, &hidden_dentry->d_name, &lkup);
225
//if (LktrCond) {dput(tmp_dentry); tmp_dentry = ERR_PTR(-1);}
226
err = PTR_ERR(tmp_dentry);
227
if (!IS_ERR(tmp_dentry)) {
228
/* under the same dir, no need to lock_rename() */
229
err = vfsub_rename(hidden_dir, hidden_dentry,
230
hidden_dir, tmp_dentry, lkup.dlgt);
231
//if (LktrCond) err = -1; //unavailable
240
/* ---------------------------------------------------------------------- */
242
int au_unlink_wh_dentry(struct inode *hidden_dir, struct dentry *wh_dentry,
243
struct dentry *dentry, int dlgt)
247
LKTRTrace("hi%lu, wh %.*s, d %p\n", hidden_dir->i_ino,
248
DLNPair(wh_dentry), dentry);
249
DEBUG_ON((dentry && dbwh(dentry) == -1)
250
|| !wh_dentry->d_inode
251
|| !S_ISREG(wh_dentry->d_inode->i_mode));
252
IMustLock(hidden_dir);
254
err = vfsub_unlink(hidden_dir, wh_dentry, dlgt);
255
//if (LktrCond) err = -1; // unavailable
257
set_dbwh(dentry, -1);
263
static int unlink_wh_name(struct dentry *hidden_parent, struct qstr *wh,
264
struct lkup_args *lkup)
267
struct inode *hidden_dir;
268
struct dentry *hidden_dentry;
270
LKTRTrace("%.*s/%.*s\n", DLNPair(hidden_parent), LNPair(wh));
271
hidden_dir = hidden_parent->d_inode;
272
IMustLock(hidden_dir);
274
// au_test_perm() is already done
275
hidden_dentry = lkup_one(wh->name, hidden_parent, wh->len, lkup);
276
//if (LktrCond) {dput(hidden_dentry); hidden_dentry = ERR_PTR(-1);}
277
if (!IS_ERR(hidden_dentry)) {
279
if (hidden_dentry->d_inode)
280
err = vfsub_unlink(hidden_dir, hidden_dentry,
284
err = PTR_ERR(hidden_dentry);
290
/* ---------------------------------------------------------------------- */
292
static void clean_wh(struct inode *h_dir, struct dentry *wh)
296
int err = vfsub_unlink(h_dir, wh, /*dlgt*/0);
298
Warn("failed unlink %.*s (%d), ignored.\n",
303
static void clean_plink(struct inode *h_dir, struct dentry *plink)
306
if (plink->d_inode) {
307
int err = vfsub_rmdir(h_dir, plink, /*dlgt*/0);
309
Warn("failed rmdir %.*s (%d), ignored.\n",
310
DLNPair(plink), err);
314
static int test_linkable(struct inode *h_dir)
316
if (h_dir->i_op && h_dir->i_op->link)
321
static int plink_dir(struct inode *h_dir, struct dentry *plink)
326
if (!plink->d_inode) {
328
if (unlikely(au_is_nfs(plink->d_sb)))
330
err = vfsub_mkdir(h_dir, plink, mode, /*dlgt*/0);
331
} else if (S_ISDIR(plink->d_inode->i_mode))
334
Err("unknown %.*s exists\n", DLNPair(plink));
340
* initialize the whiteout base file/dir for @br.
342
int init_wh(struct dentry *h_root, struct aufs_branch *br,
343
struct vfsmount *nfsmnt, struct super_block *sb)
346
struct dentry *wh, *plink;
348
static struct qstr base_name[] = {
349
{.name = AUFS_WH_BASENAME, .len = sizeof(AUFS_WH_BASENAME) - 1},
350
{.name = AUFS_WH_PLINKDIR, .len = sizeof(AUFS_WH_PLINKDIR) - 1}
352
struct lkup_args lkup = {
354
.dlgt = 0 // always no dlgt
356
const int do_plink = au_flag_test(sb, AuFlag_PLINK);
358
LKTRTrace("nfsmnt %p\n", nfsmnt);
359
BrWhMustWriteLock(br);
361
h_dir = h_root->d_inode;
365
wh = lkup_wh(h_root, base_name + 0, &lkup);
366
//if (LktrCond) {dput(wh); wh = ERR_PTR(-1);}
370
DEBUG_ON(br->br_wh && br->br_wh != wh);
372
plink = lkup_wh(h_root, base_name + 1, &lkup);
373
err = PTR_ERR(plink);
376
DEBUG_ON(br->br_plink && br->br_plink != plink);
380
br->br_wh = br->br_plink = NULL;
383
switch (br->br_perm) {
389
clean_plink(h_dir, plink);
392
case AuBr_RWNoLinkWH:
395
err = test_linkable(h_dir);
399
err = plink_dir(h_dir, plink);
402
br->br_plink = dget(plink);
404
clean_plink(h_dir, plink);
409
* for the moment, aufs supports the branch filesystem
410
* which does not support link(2).
411
* testing on FAT which does not support i_op->setattr() fully either,
413
* finally, such filesystem will not be used as the writable branch.
415
err = test_linkable(h_dir);
421
err = vfsub_create(h_dir, wh, WH_MASK, NULL, /*dlgt*/0);
422
else if (S_ISREG(wh->d_inode->i_mode))
425
Err("unknown %.*s/%.*s exists\n",
426
DLNPair(h_root), DLNPair(wh));
431
err = plink_dir(h_dir, plink);
434
br->br_plink = dget(plink);
436
clean_plink(h_dir, plink);
437
br->br_wh = dget(wh);
452
Err("%.*s doesn't support link(2), use noplink and rw+nolwh\n",
456
Err("an error(%d) on the writable branch %.*s(%s)\n",
457
err, DLNPair(h_root), au_sbtype(h_root->d_sb));
461
struct reinit_br_wh {
462
struct super_block *sb;
463
struct aufs_branch *br;
466
static void reinit_br_wh(void *arg)
469
struct reinit_br_wh *a = arg;
470
struct inode *hidden_dir, *dir;
471
struct dentry *hidden_root;
472
aufs_bindex_t bindex;
475
DEBUG_ON(!a->br->br_wh || !a->br->br_wh->d_inode || current->fsuid);
479
si_write_lock(a->sb);
480
if (unlikely(!br_writable(a->br->br_perm)))
482
bindex = find_brindex(a->sb, a->br->br_id);
483
if (unlikely(bindex < 0))
486
dir = a->sb->s_root->d_inode;
487
hidden_root = a->br->br_wh->d_parent;
488
hidden_dir = hidden_root->d_inode;
489
DEBUG_ON(!hidden_dir->i_op || !hidden_dir->i_op->link);
490
hdir_lock(hidden_dir, dir, bindex);
491
br_wh_write_lock(a->br);
492
err = vfsub_unlink(hidden_dir, a->br->br_wh, /*dlgt*/0);
493
//if (LktrCond) err = -1;
497
err = init_wh(hidden_root, a->br, au_do_nfsmnt(a->br->br_mnt),
499
br_wh_write_unlock(a->br);
500
hdir_unlock(hidden_dir, dir, bindex);
503
atomic_dec(&a->br->br_wh_running);
505
si_write_unlock(a->sb);
508
IOErr("err %d\n", err);
511
static void kick_reinit_br_wh(struct super_block *sb, struct aufs_branch *br)
514
struct reinit_br_wh *arg;
517
if (atomic_inc_return(&br->br_wh_running) != 1)
521
arg = kmalloc(sizeof(*arg), GFP_KERNEL);
523
// dec(wh_running), kfree(arg) and br_put() in reinit function
527
au_wkq_nowait(reinit_br_wh, arg, /*dlgt*/0);
533
atomic_dec(&br->br_wh_running);
537
* create the whiteoute @wh.
539
static int link_or_create_wh(struct dentry *wh, struct super_block *sb,
540
aufs_bindex_t bindex)
543
struct aufs_branch *br;
544
struct dentry *hidden_parent;
545
struct inode *hidden_dir;
547
LKTRTrace("%.*s\n", DLNPair(wh));
549
hidden_parent = wh->d_parent;
550
hidden_dir = hidden_parent->d_inode;
551
IMustLock(hidden_dir);
553
dlgt = need_dlgt(sb);
554
br = stobr(sb, bindex);
557
err = vfsub_link(br->br_wh, hidden_dir, wh, dlgt);
558
if (!err || err != -EMLINK)
561
// link count full. re-initialize br_wh.
562
kick_reinit_br_wh(sb, br);
565
// return this error in this context
566
err = vfsub_create(hidden_dir, wh, WH_MASK, NULL, dlgt);
569
br_wh_read_unlock(br);
574
/* ---------------------------------------------------------------------- */
577
* create or remove the diropq.
579
static struct dentry *do_diropq(struct dentry *dentry, aufs_bindex_t bindex,
580
int do_create, int dlgt)
582
struct dentry *opq_dentry, *hidden_dentry;
583
struct inode *hidden_dir;
585
struct super_block *sb;
586
struct lkup_args lkup;
588
LKTRTrace("%.*s, bindex %d, do_create %d\n", DLNPair(dentry),
590
hidden_dentry = au_h_dptr_i(dentry, bindex);
591
DEBUG_ON(!hidden_dentry);
592
hidden_dir = hidden_dentry->d_inode;
593
DEBUG_ON(!hidden_dir || !S_ISDIR(hidden_dir->i_mode));
594
IMustLock(hidden_dir);
596
// already checked by au_test_perm().
598
lkup.nfsmnt = au_nfsmnt(sb, bindex);
600
opq_dentry = lkup_one(diropq_name.name, hidden_dentry, diropq_name.len,
602
//if (LktrCond) {dput(opq_dentry); opq_dentry = ERR_PTR(-1);}
603
if (IS_ERR(opq_dentry))
607
DEBUG_ON(opq_dentry->d_inode);
608
err = link_or_create_wh(opq_dentry, sb, bindex);
609
//if (LktrCond) {vfs_unlink(hidden_dir, opq_dentry); err = -1;}
611
set_dbdiropq(dentry, bindex);
612
goto out; /* success */
615
DEBUG_ON(/* !S_ISDIR(dentry->d_inode->i_mode)
616
* || */!opq_dentry->d_inode);
617
err = vfsub_unlink(hidden_dir, opq_dentry, lkup.dlgt);
618
//if (LktrCond) err = -1;
620
set_dbdiropq(dentry, -1);
623
opq_dentry = ERR_PTR(err);
626
TraceErrPtr(opq_dentry);
630
struct do_diropq_args {
631
struct dentry **errp;
632
struct dentry *dentry;
633
aufs_bindex_t bindex;
637
static void call_do_diropq(void *args)
639
struct do_diropq_args *a = args;
640
*a->errp = do_diropq(a->dentry, a->bindex, a->do_create, a->dlgt);
643
struct dentry *sio_diropq(struct dentry *dentry, aufs_bindex_t bindex,
644
int do_create, int dlgt)
646
struct dentry *diropq, *hidden_dentry;
648
LKTRTrace("%.*s, bindex %d, do_create %d\n",
649
DLNPair(dentry), bindex, do_create);
651
hidden_dentry = au_h_dptr_i(dentry, bindex);
652
if (!au_test_perm(hidden_dentry->d_inode, MAY_EXEC | MAY_WRITE, dlgt))
653
diropq = do_diropq(dentry, bindex, do_create, dlgt);
655
struct do_diropq_args args = {
659
.do_create = do_create,
662
au_wkq_wait(call_do_diropq, &args, /*dlgt*/0);
669
/* ---------------------------------------------------------------------- */
672
* lookup whiteout dentry.
673
* @hidden_parent: hidden parent dentry which must exist and be locked
674
* @base_name: name of dentry which will be whiteouted
675
* returns dentry for whiteout.
677
struct dentry *lkup_wh(struct dentry *hidden_parent, struct qstr *base_name,
678
struct lkup_args *lkup)
682
struct dentry *wh_dentry;
684
LKTRTrace("%.*s/%.*s\n", DLNPair(hidden_parent), LNPair(base_name));
685
IMustLock(hidden_parent->d_inode);
687
err = au_alloc_whname(base_name->name, base_name->len, &wh_name);
688
//if (LktrCond) {au_free_whname(&wh_name); err = -1;}
689
wh_dentry = ERR_PTR(err);
692
wh_dentry = lkup_one(wh_name.name, hidden_parent, wh_name.len,
694
au_free_whname(&wh_name);
696
TraceErrPtr(wh_dentry);
701
* link/create a whiteout for @dentry on @bindex.
703
struct dentry *simple_create_wh(struct dentry *dentry, aufs_bindex_t bindex,
704
struct dentry *hidden_parent,
705
struct lkup_args *lkup)
707
struct dentry *wh_dentry;
709
struct super_block *sb;
711
LKTRTrace("%.*s/%.*s on b%d\n", DLNPair(hidden_parent),
712
DLNPair(dentry), bindex);
715
wh_dentry = lkup_wh(hidden_parent, &dentry->d_name, lkup);
716
//au_nfsmnt(sb, bindex), need_dlgt(sb));
717
//if (LktrCond) {dput(wh_dentry); wh_dentry = ERR_PTR(-1);}
718
if (!IS_ERR(wh_dentry) && !wh_dentry->d_inode) {
719
IMustLock(hidden_parent->d_inode);
720
err = link_or_create_wh(wh_dentry, sb, bindex);
722
set_dbwh(dentry, bindex);
725
wh_dentry = ERR_PTR(err);
729
TraceErrPtr(wh_dentry);
733
/* ---------------------------------------------------------------------- */
735
/* Delete all whiteouts in this directory in branch bindex. */
736
static int del_wh_children(struct aufs_nhash *whlist,
737
struct dentry *hidden_parent, aufs_bindex_t bindex,
738
struct lkup_args *lkup)
743
struct inode *hidden_dir;
744
struct hlist_head *head;
745
struct aufs_wh *tpos;
746
struct hlist_node *pos;
747
struct aufs_destr *str;
749
LKTRTrace("%.*s\n", DLNPair(hidden_parent));
750
hidden_dir = hidden_parent->d_inode;
751
IMustLock(hidden_dir);
752
DEBUG_ON(IS_RDONLY(hidden_dir));
753
//SiMustReadLock(??);
756
wh_name.name = p = __getname();
757
//if (LktrCond) {__putname(p); wh_name.name = p = NULL;}
758
if (unlikely(!wh_name.name))
760
memcpy(p, AUFS_WH_PFX, AUFS_WH_LEN);
763
// already checked by au_test_perm().
765
for (i = 0; !err && i < AUFS_NHASH_SIZE; i++) {
766
head = whlist->heads + i;
767
hlist_for_each_entry(tpos, pos, head, wh_hash) {
768
if (tpos->wh_bindex != bindex)
771
if (str->len + AUFS_WH_LEN <= PATH_MAX) {
772
memcpy(p, str->name, str->len);
773
wh_name.len = AUFS_WH_LEN + str->len;
774
err = unlink_wh_name(hidden_parent, &wh_name,
776
//if (LktrCond) err = -1;
781
IOErr("whiteout name too long %.*s\n",
782
str->len, str->name);
787
__putname(wh_name.name);
794
struct del_wh_children_args {
796
struct aufs_nhash *whlist;
797
struct dentry *hidden_parent;
798
aufs_bindex_t bindex;
799
struct lkup_args *lkup;
802
static void call_del_wh_children(void *args)
804
struct del_wh_children_args *a = args;
805
*a->errp = del_wh_children(a->whlist, a->hidden_parent, a->bindex,
809
/* ---------------------------------------------------------------------- */
812
* rmdir the whiteouted temporary named dir @hidden_dentry.
813
* @whlist: whiteouted children.
815
int rmdir_whtmp(struct dentry *hidden_dentry, struct aufs_nhash *whlist,
816
aufs_bindex_t bindex, struct inode *dir, struct inode *inode)
819
struct inode *hidden_inode, *hidden_dir;
820
struct lkup_args lkup;
821
struct super_block *sb;
823
LKTRTrace("hd %.*s, b%d, i%lu\n",
824
DLNPair(hidden_dentry), bindex, dir->i_ino);
827
hidden_dir = hidden_dentry->d_parent->d_inode;
828
IMustLock(hidden_dir);
831
lkup.nfsmnt = au_nfsmnt(sb, bindex);
832
lkup.dlgt = need_dlgt(sb);
833
hidden_inode = hidden_dentry->d_inode;
834
DEBUG_ON(hidden_inode != au_h_iptr_i(inode, bindex));
835
hdir2_lock(hidden_inode, inode, bindex);
836
if (!au_test_perm(hidden_inode, MAY_EXEC | MAY_WRITE, lkup.dlgt))
837
err = del_wh_children(whlist, hidden_dentry, bindex, &lkup);
840
int dlgt = lkup.dlgt;
841
struct del_wh_children_args args = {
844
.hidden_parent = hidden_dentry,
850
au_wkq_wait(call_del_wh_children, &args, /*dlgt*/0);
853
hdir_unlock(hidden_inode, inode, bindex);
856
err = vfsub_rmdir(hidden_dir, hidden_dentry, lkup.dlgt);
857
//d_drop(hidden_dentry);
858
//if (LktrCond) err = -1;
862
if (ibstart(dir) == bindex) {
863
au_cpup_attr_timesizes(dir);
864
//au_cpup_attr_nlink(dir);
867
return 0; /* success */
870
Warn("failed removing %.*s(%d), ignored\n",
871
DLNPair(hidden_dentry), err);
875
static void do_rmdir_whtmp(void *arg)
878
struct rmdir_whtmp_arg *a = arg;
879
struct super_block *sb;
881
LKTRTrace("%.*s, b%d, dir i%lu\n",
882
DLNPair(a->h_dentry), a->bindex, a->dir->i_ino);
887
err = test_ro(sb, a->bindex, NULL);
889
struct inode *hidden_dir = a->h_dentry->d_parent->d_inode;
891
ii_write_lock_child(a->inode);
892
ii_write_lock_parent(a->dir);
893
hdir_lock(hidden_dir, a->dir, a->bindex);
894
err = rmdir_whtmp(a->h_dentry, &a->whlist, a->bindex,
896
hdir_unlock(hidden_dir, a->dir, a->bindex);
897
ii_write_unlock(a->dir);
898
ii_write_unlock(a->inode);
901
nhash_fin(&a->whlist);
908
IOErr("err %d\n", err);
911
void kick_rmdir_whtmp(struct dentry *hidden_dentry, struct aufs_nhash *whlist,
912
aufs_bindex_t bindex, struct inode *dir,
913
struct inode *inode, struct rmdir_whtmp_arg *arg)
915
LKTRTrace("%.*s\n", DLNPair(hidden_dentry));
918
// all post-process will be done in do_rmdir_whtmp().
919
arg->h_dentry = dget(hidden_dentry);
920
nhash_init(&arg->whlist);
921
nhash_move(&arg->whlist, whlist);
922
arg->bindex = bindex;
923
arg->dir = igrab(dir);
924
arg->inode = igrab(inode);
926
au_wkq_nowait(do_rmdir_whtmp, arg, /*dlgt*/0);