Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* -*- c -*- --------------------------------------------------------------- * |
2 | * | |
3 | * linux/fs/autofs/expire.c | |
4 | * | |
5 | * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved | |
6 | * Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org> | |
7 | * Copyright 2001-2003 Ian Kent <raven@themaw.net> | |
8 | * | |
9 | * This file is part of the Linux kernel and is made available under | |
10 | * the terms of the GNU General Public License, version 2, or at your | |
11 | * option, any later version, incorporated herein by reference. | |
12 | * | |
13 | * ------------------------------------------------------------------------- */ | |
14 | ||
15 | #include "autofs_i.h" | |
16 | ||
17 | static unsigned long now; | |
18 | ||
19 | /* Check if a dentry can be expired return 1 if it can else return 0 */ | |
20 | static inline int autofs4_can_expire(struct dentry *dentry, | |
21 | unsigned long timeout, int do_now) | |
22 | { | |
23 | struct autofs_info *ino = autofs4_dentry_ino(dentry); | |
24 | ||
25 | /* dentry in the process of being deleted */ | |
26 | if (ino == NULL) | |
27 | return 0; | |
28 | ||
29 | /* No point expiring a pending mount */ | |
30 | if (dentry->d_flags & DCACHE_AUTOFS_PENDING) | |
31 | return 0; | |
32 | ||
33 | if (!do_now) { | |
34 | /* Too young to die */ | |
35 | if (time_after(ino->last_used + timeout, now)) | |
36 | return 0; | |
37 | ||
38 | /* update last_used here :- | |
39 | - obviously makes sense if it is in use now | |
40 | - less obviously, prevents rapid-fire expire | |
41 | attempts if expire fails the first time */ | |
42 | ino->last_used = now; | |
43 | } | |
44 | ||
45 | return 1; | |
46 | } | |
47 | ||
48 | /* Check a mount point for busyness return 1 if not busy, otherwise */ | |
49 | static int autofs4_check_mount(struct vfsmount *mnt, struct dentry *dentry) | |
50 | { | |
51 | int status = 0; | |
52 | ||
53 | DPRINTK("dentry %p %.*s", | |
54 | dentry, (int)dentry->d_name.len, dentry->d_name.name); | |
55 | ||
56 | mntget(mnt); | |
57 | dget(dentry); | |
58 | ||
9b1e3afd | 59 | if (!autofs4_follow_mount(&mnt, &dentry)) |
1da177e4 LT |
60 | goto done; |
61 | ||
1da177e4 LT |
62 | /* This is an autofs submount, we can't expire it */ |
63 | if (is_autofs4_dentry(dentry)) | |
64 | goto done; | |
65 | ||
66 | /* The big question */ | |
67 | if (may_umount_tree(mnt) == 0) | |
68 | status = 1; | |
69 | done: | |
70 | DPRINTK("returning = %d", status); | |
71 | mntput(mnt); | |
72 | dput(dentry); | |
73 | return status; | |
74 | } | |
75 | ||
76 | /* Check a directory tree of mount points for busyness | |
77 | * The tree is not busy iff no mountpoints are busy | |
78 | * Return 1 if the tree is busy or 0 otherwise | |
79 | */ | |
80 | static int autofs4_check_tree(struct vfsmount *mnt, | |
81 | struct dentry *top, | |
82 | unsigned long timeout, | |
83 | int do_now) | |
84 | { | |
85 | struct dentry *this_parent = top; | |
86 | struct list_head *next; | |
87 | ||
88 | DPRINTK("parent %p %.*s", | |
89 | top, (int)top->d_name.len, top->d_name.name); | |
90 | ||
91 | /* Negative dentry - give up */ | |
92 | if (!simple_positive(top)) | |
93 | return 0; | |
94 | ||
95 | /* Timeout of a tree mount is determined by its top dentry */ | |
96 | if (!autofs4_can_expire(top, timeout, do_now)) | |
97 | return 0; | |
98 | ||
3a9720ce IK |
99 | /* Is someone visiting anywhere in the tree ? */ |
100 | if (may_umount_tree(mnt)) | |
101 | return 0; | |
102 | ||
1da177e4 LT |
103 | spin_lock(&dcache_lock); |
104 | repeat: | |
105 | next = this_parent->d_subdirs.next; | |
106 | resume: | |
107 | while (next != &this_parent->d_subdirs) { | |
5160ee6f | 108 | struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child); |
1da177e4 LT |
109 | |
110 | /* Negative dentry - give up */ | |
111 | if (!simple_positive(dentry)) { | |
112 | next = next->next; | |
113 | continue; | |
114 | } | |
115 | ||
116 | DPRINTK("dentry %p %.*s", | |
117 | dentry, (int)dentry->d_name.len, dentry->d_name.name); | |
118 | ||
119 | if (!simple_empty_nolock(dentry)) { | |
120 | this_parent = dentry; | |
121 | goto repeat; | |
122 | } | |
123 | ||
124 | dentry = dget(dentry); | |
125 | spin_unlock(&dcache_lock); | |
126 | ||
127 | if (d_mountpoint(dentry)) { | |
128 | /* First busy => tree busy */ | |
129 | if (!autofs4_check_mount(mnt, dentry)) { | |
130 | dput(dentry); | |
131 | return 0; | |
132 | } | |
133 | } | |
134 | ||
135 | dput(dentry); | |
136 | spin_lock(&dcache_lock); | |
137 | next = next->next; | |
138 | } | |
139 | ||
140 | if (this_parent != top) { | |
5160ee6f | 141 | next = this_parent->d_u.d_child.next; |
1da177e4 LT |
142 | this_parent = this_parent->d_parent; |
143 | goto resume; | |
144 | } | |
145 | spin_unlock(&dcache_lock); | |
146 | ||
147 | return 1; | |
148 | } | |
149 | ||
150 | static struct dentry *autofs4_check_leaves(struct vfsmount *mnt, | |
151 | struct dentry *parent, | |
152 | unsigned long timeout, | |
153 | int do_now) | |
154 | { | |
155 | struct dentry *this_parent = parent; | |
156 | struct list_head *next; | |
157 | ||
158 | DPRINTK("parent %p %.*s", | |
159 | parent, (int)parent->d_name.len, parent->d_name.name); | |
160 | ||
161 | spin_lock(&dcache_lock); | |
162 | repeat: | |
163 | next = this_parent->d_subdirs.next; | |
164 | resume: | |
165 | while (next != &this_parent->d_subdirs) { | |
5160ee6f | 166 | struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child); |
1da177e4 LT |
167 | |
168 | /* Negative dentry - give up */ | |
169 | if (!simple_positive(dentry)) { | |
170 | next = next->next; | |
171 | continue; | |
172 | } | |
173 | ||
174 | DPRINTK("dentry %p %.*s", | |
175 | dentry, (int)dentry->d_name.len, dentry->d_name.name); | |
176 | ||
177 | if (!list_empty(&dentry->d_subdirs)) { | |
178 | this_parent = dentry; | |
179 | goto repeat; | |
180 | } | |
181 | ||
182 | dentry = dget(dentry); | |
183 | spin_unlock(&dcache_lock); | |
184 | ||
185 | if (d_mountpoint(dentry)) { | |
186 | /* Can we expire this guy */ | |
187 | if (!autofs4_can_expire(dentry, timeout, do_now)) | |
188 | goto cont; | |
189 | ||
190 | /* Can we umount this guy */ | |
191 | if (autofs4_check_mount(mnt, dentry)) | |
192 | return dentry; | |
193 | ||
194 | } | |
195 | cont: | |
196 | dput(dentry); | |
197 | spin_lock(&dcache_lock); | |
198 | next = next->next; | |
199 | } | |
200 | ||
201 | if (this_parent != parent) { | |
5160ee6f | 202 | next = this_parent->d_u.d_child.next; |
1da177e4 LT |
203 | this_parent = this_parent->d_parent; |
204 | goto resume; | |
205 | } | |
206 | spin_unlock(&dcache_lock); | |
207 | ||
208 | return NULL; | |
209 | } | |
210 | ||
211 | /* | |
212 | * Find an eligible tree to time-out | |
213 | * A tree is eligible if :- | |
214 | * - it is unused by any user process | |
215 | * - it has been unused for exp_timeout time | |
216 | */ | |
217 | static struct dentry *autofs4_expire(struct super_block *sb, | |
218 | struct vfsmount *mnt, | |
219 | struct autofs_sb_info *sbi, | |
220 | int how) | |
221 | { | |
222 | unsigned long timeout; | |
223 | struct dentry *root = sb->s_root; | |
224 | struct dentry *expired = NULL; | |
225 | struct list_head *next; | |
226 | int do_now = how & AUTOFS_EXP_IMMEDIATE; | |
227 | int exp_leaves = how & AUTOFS_EXP_LEAVES; | |
228 | ||
229 | if ( !sbi->exp_timeout || !root ) | |
230 | return NULL; | |
231 | ||
232 | now = jiffies; | |
233 | timeout = sbi->exp_timeout; | |
234 | ||
235 | spin_lock(&dcache_lock); | |
236 | next = root->d_subdirs.next; | |
237 | ||
238 | /* On exit from the loop expire is set to a dgot dentry | |
239 | * to expire or it's NULL */ | |
240 | while ( next != &root->d_subdirs ) { | |
5160ee6f | 241 | struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child); |
1da177e4 LT |
242 | |
243 | /* Negative dentry - give up */ | |
244 | if ( !simple_positive(dentry) ) { | |
245 | next = next->next; | |
246 | continue; | |
247 | } | |
248 | ||
249 | dentry = dget(dentry); | |
250 | spin_unlock(&dcache_lock); | |
251 | ||
252 | /* Case 1: indirect mount or top level direct mount */ | |
253 | if (d_mountpoint(dentry)) { | |
254 | DPRINTK("checking mountpoint %p %.*s", | |
255 | dentry, (int)dentry->d_name.len, dentry->d_name.name); | |
256 | ||
257 | /* Can we expire this guy */ | |
258 | if (!autofs4_can_expire(dentry, timeout, do_now)) | |
259 | goto next; | |
260 | ||
261 | /* Can we umount this guy */ | |
262 | if (autofs4_check_mount(mnt, dentry)) { | |
263 | expired = dentry; | |
264 | break; | |
265 | } | |
266 | goto next; | |
267 | } | |
268 | ||
269 | if ( simple_empty(dentry) ) | |
270 | goto next; | |
271 | ||
272 | /* Case 2: tree mount, expire iff entire tree is not busy */ | |
273 | if (!exp_leaves) { | |
3a9720ce IK |
274 | /* Lock the tree as we must expire as a whole */ |
275 | spin_lock(&sbi->fs_lock); | |
1da177e4 | 276 | if (autofs4_check_tree(mnt, dentry, timeout, do_now)) { |
3a9720ce IK |
277 | struct autofs_info *inf = autofs4_dentry_ino(dentry); |
278 | ||
279 | /* Set this flag early to catch sys_chdir and the like */ | |
280 | inf->flags |= AUTOFS_INF_EXPIRING; | |
281 | spin_unlock(&sbi->fs_lock); | |
282 | expired = dentry; | |
283 | break; | |
1da177e4 | 284 | } |
3a9720ce | 285 | spin_unlock(&sbi->fs_lock); |
1da177e4 LT |
286 | /* Case 3: direct mount, expire individual leaves */ |
287 | } else { | |
288 | expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); | |
289 | if (expired) { | |
290 | dput(dentry); | |
291 | break; | |
292 | } | |
293 | } | |
294 | next: | |
295 | dput(dentry); | |
296 | spin_lock(&dcache_lock); | |
297 | next = next->next; | |
298 | } | |
299 | ||
300 | if ( expired ) { | |
301 | DPRINTK("returning %p %.*s", | |
302 | expired, (int)expired->d_name.len, expired->d_name.name); | |
303 | spin_lock(&dcache_lock); | |
304 | list_del(&expired->d_parent->d_subdirs); | |
5160ee6f | 305 | list_add(&expired->d_parent->d_subdirs, &expired->d_u.d_child); |
1da177e4 LT |
306 | spin_unlock(&dcache_lock); |
307 | return expired; | |
308 | } | |
309 | spin_unlock(&dcache_lock); | |
310 | ||
311 | return NULL; | |
312 | } | |
313 | ||
314 | /* Perform an expiry operation */ | |
315 | int autofs4_expire_run(struct super_block *sb, | |
316 | struct vfsmount *mnt, | |
317 | struct autofs_sb_info *sbi, | |
318 | struct autofs_packet_expire __user *pkt_p) | |
319 | { | |
320 | struct autofs_packet_expire pkt; | |
321 | struct dentry *dentry; | |
322 | ||
323 | memset(&pkt,0,sizeof pkt); | |
324 | ||
325 | pkt.hdr.proto_version = sbi->version; | |
326 | pkt.hdr.type = autofs_ptype_expire; | |
327 | ||
328 | if ((dentry = autofs4_expire(sb, mnt, sbi, 0)) == NULL) | |
329 | return -EAGAIN; | |
330 | ||
331 | pkt.len = dentry->d_name.len; | |
332 | memcpy(pkt.name, dentry->d_name.name, pkt.len); | |
333 | pkt.name[pkt.len] = '\0'; | |
334 | dput(dentry); | |
335 | ||
336 | if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) | |
337 | return -EFAULT; | |
338 | ||
339 | return 0; | |
340 | } | |
341 | ||
342 | /* Call repeatedly until it returns -EAGAIN, meaning there's nothing | |
343 | more to be done */ | |
344 | int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, | |
345 | struct autofs_sb_info *sbi, int __user *arg) | |
346 | { | |
347 | struct dentry *dentry; | |
348 | int ret = -EAGAIN; | |
349 | int do_now = 0; | |
350 | ||
351 | if (arg && get_user(do_now, arg)) | |
352 | return -EFAULT; | |
353 | ||
354 | if ((dentry = autofs4_expire(sb, mnt, sbi, do_now)) != NULL) { | |
355 | struct autofs_info *de_info = autofs4_dentry_ino(dentry); | |
356 | ||
357 | /* This is synchronous because it makes the daemon a | |
358 | little easier */ | |
359 | de_info->flags |= AUTOFS_INF_EXPIRING; | |
360 | ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); | |
361 | de_info->flags &= ~AUTOFS_INF_EXPIRING; | |
362 | dput(dentry); | |
363 | } | |
364 | ||
365 | return ret; | |
366 | } | |
367 |