Blame view
fs/namespace.c
65.9 KB
1da177e4c
|
1 2 3 4 5 6 7 8 9 |
/* * linux/fs/namespace.c * * (C) Copyright Al Viro 2000, 2001 * Released under GPL v2. * * Based on code from fs/super.c, copyright Linus Torvalds and others. * Heavily rewritten. */ |
1da177e4c
|
10 11 12 |
#include <linux/syscalls.h> #include <linux/slab.h> #include <linux/sched.h> |
99b7db7b8
|
13 14 |
#include <linux/spinlock.h> #include <linux/percpu.h> |
1da177e4c
|
15 |
#include <linux/init.h> |
15a67dd8c
|
16 |
#include <linux/kernel.h> |
1da177e4c
|
17 |
#include <linux/acct.h> |
16f7e0fe2
|
18 |
#include <linux/capability.h> |
3d733633a
|
19 |
#include <linux/cpumask.h> |
1da177e4c
|
20 |
#include <linux/module.h> |
f20a9ead0
|
21 |
#include <linux/sysfs.h> |
1da177e4c
|
22 |
#include <linux/seq_file.h> |
6b3286ed1
|
23 |
#include <linux/mnt_namespace.h> |
1da177e4c
|
24 |
#include <linux/namei.h> |
b43f3cbd2
|
25 |
#include <linux/nsproxy.h> |
1da177e4c
|
26 27 |
#include <linux/security.h> #include <linux/mount.h> |
07f3f05c1
|
28 |
#include <linux/ramfs.h> |
13f14b4d8
|
29 |
#include <linux/log2.h> |
73cd49ecd
|
30 |
#include <linux/idr.h> |
5ad4e53bd
|
31 |
#include <linux/fs_struct.h> |
2504c5d63
|
32 |
#include <linux/fsnotify.h> |
1da177e4c
|
33 34 |
#include <asm/uaccess.h> #include <asm/unistd.h> |
07b20889e
|
35 |
#include "pnode.h" |
948730b0e
|
36 |
#include "internal.h" |
1da177e4c
|
37 |
|
13f14b4d8
|
38 39 |
#define HASH_SHIFT ilog2(PAGE_SIZE / sizeof(struct list_head)) #define HASH_SIZE (1UL << HASH_SHIFT) |
5addc5dd8
|
40 |
static int event; |
73cd49ecd
|
41 |
static DEFINE_IDA(mnt_id_ida); |
719f5d7f0
|
42 |
static DEFINE_IDA(mnt_group_ida); |
99b7db7b8
|
43 |
static DEFINE_SPINLOCK(mnt_id_lock); |
f21f62208
|
44 45 |
static int mnt_id_start = 0; static int mnt_group_start = 1; |
1da177e4c
|
46 |
|
fa3536cc1
|
47 |
static struct list_head *mount_hashtable __read_mostly; |
e18b890bb
|
48 |
static struct kmem_cache *mnt_cache __read_mostly; |
390c68436
|
49 |
static struct rw_semaphore namespace_sem; |
1da177e4c
|
50 |
|
f87fd4c2a
|
51 |
/* /sys/fs */ |
00d266662
|
52 53 |
struct kobject *fs_kobj; EXPORT_SYMBOL_GPL(fs_kobj); |
f87fd4c2a
|
54 |
|
99b7db7b8
|
55 56 57 58 59 60 61 62 63 |
/* * vfsmount lock may be taken for read to prevent changes to the * vfsmount hash, ie. during mountpoint lookups or walking back * up the tree. * * It should be taken for write in all cases where the vfsmount * tree or hash is modified or when a vfsmount structure is modified. */ DEFINE_BRLOCK(vfsmount_lock); |
1da177e4c
|
64 65 |
static inline unsigned long hash(struct vfsmount *mnt, struct dentry *dentry) { |
b58fed8b1
|
66 67 |
unsigned long tmp = ((unsigned long)mnt / L1_CACHE_BYTES); tmp += ((unsigned long)dentry / L1_CACHE_BYTES); |
13f14b4d8
|
68 69 |
tmp = tmp + (tmp >> HASH_SHIFT); return tmp & (HASH_SIZE - 1); |
1da177e4c
|
70 |
} |
3d733633a
|
71 |
#define MNT_WRITER_UNDERFLOW_LIMIT -(1<<16) |
99b7db7b8
|
72 73 74 75 |
/* * allocation is serialized by namespace_sem, but we need the spinlock to * serialize with freeing. */ |
73cd49ecd
|
76 77 78 79 80 81 |
static int mnt_alloc_id(struct vfsmount *mnt) { int res; retry: ida_pre_get(&mnt_id_ida, GFP_KERNEL); |
99b7db7b8
|
82 |
spin_lock(&mnt_id_lock); |
f21f62208
|
83 84 85 |
res = ida_get_new_above(&mnt_id_ida, mnt_id_start, &mnt->mnt_id); if (!res) mnt_id_start = mnt->mnt_id + 1; |
99b7db7b8
|
86 |
spin_unlock(&mnt_id_lock); |
73cd49ecd
|
87 88 89 90 91 92 93 94 |
if (res == -EAGAIN) goto retry; return res; } static void mnt_free_id(struct vfsmount *mnt) { |
f21f62208
|
95 |
int id = mnt->mnt_id; |
99b7db7b8
|
96 |
spin_lock(&mnt_id_lock); |
f21f62208
|
97 98 99 |
ida_remove(&mnt_id_ida, id); if (mnt_id_start > id) mnt_id_start = id; |
99b7db7b8
|
100 |
spin_unlock(&mnt_id_lock); |
73cd49ecd
|
101 |
} |
719f5d7f0
|
102 103 104 105 106 107 108 |
/* * Allocate a new peer group ID * * mnt_group_ida is protected by namespace_sem */ static int mnt_alloc_group_id(struct vfsmount *mnt) { |
f21f62208
|
109 |
int res; |
719f5d7f0
|
110 111 |
if (!ida_pre_get(&mnt_group_ida, GFP_KERNEL)) return -ENOMEM; |
f21f62208
|
112 113 114 115 116 117 118 |
res = ida_get_new_above(&mnt_group_ida, mnt_group_start, &mnt->mnt_group_id); if (!res) mnt_group_start = mnt->mnt_group_id + 1; return res; |
719f5d7f0
|
119 120 121 122 123 124 125 |
} /* * Release a peer group ID */ void mnt_release_group_id(struct vfsmount *mnt) { |
f21f62208
|
126 127 128 129 |
int id = mnt->mnt_group_id; ida_remove(&mnt_group_ida, id); if (mnt_group_start > id) mnt_group_start = id; |
719f5d7f0
|
130 131 |
mnt->mnt_group_id = 0; } |
b3e19d924
|
132 133 134 135 136 137 138 139 140 141 142 143 144 |
/* * vfsmount lock must be held for read */ static inline void mnt_add_count(struct vfsmount *mnt, int n) { #ifdef CONFIG_SMP this_cpu_add(mnt->mnt_pcp->mnt_count, n); #else preempt_disable(); mnt->mnt_count += n; preempt_enable(); #endif } |
b3e19d924
|
145 146 147 148 149 150 |
/* * vfsmount lock must be held for write */ unsigned int mnt_get_count(struct vfsmount *mnt) { #ifdef CONFIG_SMP |
f03c65993
|
151 |
unsigned int count = 0; |
b3e19d924
|
152 153 154 155 156 157 158 159 160 161 162 |
int cpu; for_each_possible_cpu(cpu) { count += per_cpu_ptr(mnt->mnt_pcp, cpu)->mnt_count; } return count; #else return mnt->mnt_count; #endif } |
9d412a43c
|
163 |
static struct vfsmount *alloc_vfsmnt(const char *name) |
1da177e4c
|
164 |
{ |
c37622296
|
165 |
struct vfsmount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL); |
1da177e4c
|
166 |
if (mnt) { |
73cd49ecd
|
167 168 169 |
int err; err = mnt_alloc_id(mnt); |
88b387824
|
170 171 172 173 174 175 176 |
if (err) goto out_free_cache; if (name) { mnt->mnt_devname = kstrdup(name, GFP_KERNEL); if (!mnt->mnt_devname) goto out_free_id; |
73cd49ecd
|
177 |
} |
b3e19d924
|
178 179 180 181 |
#ifdef CONFIG_SMP mnt->mnt_pcp = alloc_percpu(struct mnt_pcp); if (!mnt->mnt_pcp) goto out_free_devname; |
f03c65993
|
182 |
this_cpu_add(mnt->mnt_pcp->mnt_count, 1); |
b3e19d924
|
183 184 185 186 |
#else mnt->mnt_count = 1; mnt->mnt_writers = 0; #endif |
1da177e4c
|
187 188 189 190 |
INIT_LIST_HEAD(&mnt->mnt_hash); INIT_LIST_HEAD(&mnt->mnt_child); INIT_LIST_HEAD(&mnt->mnt_mounts); INIT_LIST_HEAD(&mnt->mnt_list); |
55e700b92
|
191 |
INIT_LIST_HEAD(&mnt->mnt_expire); |
03e06e68f
|
192 |
INIT_LIST_HEAD(&mnt->mnt_share); |
a58b0eb8e
|
193 194 |
INIT_LIST_HEAD(&mnt->mnt_slave_list); INIT_LIST_HEAD(&mnt->mnt_slave); |
2504c5d63
|
195 196 197 |
#ifdef CONFIG_FSNOTIFY INIT_HLIST_HEAD(&mnt->mnt_fsnotify_marks); #endif |
1da177e4c
|
198 199 |
} return mnt; |
88b387824
|
200 |
|
d3ef3d735
|
201 202 203 204 |
#ifdef CONFIG_SMP out_free_devname: kfree(mnt->mnt_devname); #endif |
88b387824
|
205 206 207 208 209 |
out_free_id: mnt_free_id(mnt); out_free_cache: kmem_cache_free(mnt_cache, mnt); return NULL; |
1da177e4c
|
210 |
} |
8366025eb
|
211 212 213 214 215 216 217 218 |
/* * Most r/o checks on a fs are for operations that take * discrete amounts of time, like a write() or unlink(). * We must keep track of when those operations start * (for permission checks) and when they end, so that * we can determine when writes are able to occur to * a filesystem. */ |
3d733633a
|
219 220 221 222 223 224 225 226 227 228 229 230 231 |
/* * __mnt_is_readonly: check whether a mount is read-only * @mnt: the mount to check for its write status * * This shouldn't be used directly ouside of the VFS. * It does not guarantee that the filesystem will stay * r/w, just that it is right *now*. This can not and * should not be used in place of IS_RDONLY(inode). * mnt_want/drop_write() will _keep_ the filesystem * r/w. */ int __mnt_is_readonly(struct vfsmount *mnt) { |
2e4b7fcd9
|
232 233 234 235 236 |
if (mnt->mnt_flags & MNT_READONLY) return 1; if (mnt->mnt_sb->s_flags & MS_RDONLY) return 1; return 0; |
3d733633a
|
237 238 |
} EXPORT_SYMBOL_GPL(__mnt_is_readonly); |
c6653a838
|
239 |
static inline void mnt_inc_writers(struct vfsmount *mnt) |
d3ef3d735
|
240 241 |
{ #ifdef CONFIG_SMP |
b3e19d924
|
242 |
this_cpu_inc(mnt->mnt_pcp->mnt_writers); |
d3ef3d735
|
243 244 245 246 |
#else mnt->mnt_writers++; #endif } |
3d733633a
|
247 |
|
c6653a838
|
248 |
static inline void mnt_dec_writers(struct vfsmount *mnt) |
3d733633a
|
249 |
{ |
d3ef3d735
|
250 |
#ifdef CONFIG_SMP |
b3e19d924
|
251 |
this_cpu_dec(mnt->mnt_pcp->mnt_writers); |
d3ef3d735
|
252 253 254 |
#else mnt->mnt_writers--; #endif |
3d733633a
|
255 |
} |
3d733633a
|
256 |
|
c6653a838
|
257 |
static unsigned int mnt_get_writers(struct vfsmount *mnt) |
3d733633a
|
258 |
{ |
d3ef3d735
|
259 260 |
#ifdef CONFIG_SMP unsigned int count = 0; |
3d733633a
|
261 |
int cpu; |
3d733633a
|
262 263 |
for_each_possible_cpu(cpu) { |
b3e19d924
|
264 |
count += per_cpu_ptr(mnt->mnt_pcp, cpu)->mnt_writers; |
3d733633a
|
265 |
} |
3d733633a
|
266 |
|
d3ef3d735
|
267 268 269 270 |
return count; #else return mnt->mnt_writers; #endif |
3d733633a
|
271 272 273 274 275 276 277 278 279 280 |
} /* * Most r/o checks on a fs are for operations that take * discrete amounts of time, like a write() or unlink(). * We must keep track of when those operations start * (for permission checks) and when they end, so that * we can determine when writes are able to occur to * a filesystem. */ |
8366025eb
|
281 282 283 284 285 286 287 288 289 290 291 292 |
/** * mnt_want_write - get write access to a mount * @mnt: the mount on which to take a write * * This tells the low-level filesystem that a write is * about to be performed to it, and makes sure that * writes are allowed before returning success. When * the write operation is finished, mnt_drop_write() * must be called. This is effectively a refcount. */ int mnt_want_write(struct vfsmount *mnt) { |
3d733633a
|
293 |
int ret = 0; |
3d733633a
|
294 |
|
d3ef3d735
|
295 |
preempt_disable(); |
c6653a838
|
296 |
mnt_inc_writers(mnt); |
d3ef3d735
|
297 |
/* |
c6653a838
|
298 |
* The store to mnt_inc_writers must be visible before we pass |
d3ef3d735
|
299 300 301 302 303 304 305 306 307 308 309 310 |
* MNT_WRITE_HOLD loop below, so that the slowpath can see our * incremented count after it has set MNT_WRITE_HOLD. */ smp_mb(); while (mnt->mnt_flags & MNT_WRITE_HOLD) cpu_relax(); /* * After the slowpath clears MNT_WRITE_HOLD, mnt_is_readonly will * be set to match its requirements. So we must not load that until * MNT_WRITE_HOLD is cleared. */ smp_rmb(); |
3d733633a
|
311 |
if (__mnt_is_readonly(mnt)) { |
c6653a838
|
312 |
mnt_dec_writers(mnt); |
3d733633a
|
313 314 315 |
ret = -EROFS; goto out; } |
3d733633a
|
316 |
out: |
d3ef3d735
|
317 |
preempt_enable(); |
3d733633a
|
318 |
return ret; |
8366025eb
|
319 320 321 322 |
} EXPORT_SYMBOL_GPL(mnt_want_write); /** |
96029c4e0
|
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 |
* mnt_clone_write - get write access to a mount * @mnt: the mount on which to take a write * * This is effectively like mnt_want_write, except * it must only be used to take an extra write reference * on a mountpoint that we already know has a write reference * on it. This allows some optimisation. * * After finished, mnt_drop_write must be called as usual to * drop the reference. */ int mnt_clone_write(struct vfsmount *mnt) { /* superblock may be r/o */ if (__mnt_is_readonly(mnt)) return -EROFS; preempt_disable(); |
c6653a838
|
340 |
mnt_inc_writers(mnt); |
96029c4e0
|
341 342 343 344 345 346 347 348 349 350 351 352 353 354 |
preempt_enable(); return 0; } EXPORT_SYMBOL_GPL(mnt_clone_write); /** * mnt_want_write_file - get write access to a file's mount * @file: the file who's mount on which to take a write * * This is like mnt_want_write, but it takes a file and can * do some optimisations if the file is open for write already */ int mnt_want_write_file(struct file *file) { |
2d8dd38a5
|
355 356 |
struct inode *inode = file->f_dentry->d_inode; if (!(file->f_mode & FMODE_WRITE) || special_file(inode->i_mode)) |
96029c4e0
|
357 358 359 360 361 362 363 |
return mnt_want_write(file->f_path.mnt); else return mnt_clone_write(file->f_path.mnt); } EXPORT_SYMBOL_GPL(mnt_want_write_file); /** |
8366025eb
|
364 365 366 367 368 369 370 371 372 |
* mnt_drop_write - give up write access to a mount * @mnt: the mount on which to give up write access * * Tells the low-level filesystem that we are done * performing writes to it. Must be matched with * mnt_want_write() call above. */ void mnt_drop_write(struct vfsmount *mnt) { |
d3ef3d735
|
373 |
preempt_disable(); |
c6653a838
|
374 |
mnt_dec_writers(mnt); |
d3ef3d735
|
375 |
preempt_enable(); |
8366025eb
|
376 377 |
} EXPORT_SYMBOL_GPL(mnt_drop_write); |
2a79f17e4
|
378 379 380 381 382 |
void mnt_drop_write_file(struct file *file) { mnt_drop_write(file->f_path.mnt); } EXPORT_SYMBOL(mnt_drop_write_file); |
2e4b7fcd9
|
383 |
static int mnt_make_readonly(struct vfsmount *mnt) |
8366025eb
|
384 |
{ |
3d733633a
|
385 |
int ret = 0; |
99b7db7b8
|
386 |
br_write_lock(vfsmount_lock); |
d3ef3d735
|
387 |
mnt->mnt_flags |= MNT_WRITE_HOLD; |
3d733633a
|
388 |
/* |
d3ef3d735
|
389 390 |
* After storing MNT_WRITE_HOLD, we'll read the counters. This store * should be visible before we do. |
3d733633a
|
391 |
*/ |
d3ef3d735
|
392 |
smp_mb(); |
3d733633a
|
393 |
/* |
d3ef3d735
|
394 395 396 397 398 399 400 401 402 403 404 405 406 407 |
* With writers on hold, if this value is zero, then there are * definitely no active writers (although held writers may subsequently * increment the count, they'll have to wait, and decrement it after * seeing MNT_READONLY). * * It is OK to have counter incremented on one CPU and decremented on * another: the sum will add up correctly. The danger would be when we * sum up each counter, if we read a counter before it is incremented, * but then read another CPU's count which it has been subsequently * decremented from -- we would see more decrements than we should. * MNT_WRITE_HOLD protects against this scenario, because * mnt_want_write first increments count, then smp_mb, then spins on * MNT_WRITE_HOLD, so it can't be decremented by another CPU while * we're counting up here. |
3d733633a
|
408 |
*/ |
c6653a838
|
409 |
if (mnt_get_writers(mnt) > 0) |
d3ef3d735
|
410 411 |
ret = -EBUSY; else |
2e4b7fcd9
|
412 |
mnt->mnt_flags |= MNT_READONLY; |
d3ef3d735
|
413 414 415 416 417 418 |
/* * MNT_READONLY must become visible before ~MNT_WRITE_HOLD, so writers * that become unheld will see MNT_READONLY. */ smp_wmb(); mnt->mnt_flags &= ~MNT_WRITE_HOLD; |
99b7db7b8
|
419 |
br_write_unlock(vfsmount_lock); |
3d733633a
|
420 |
return ret; |
8366025eb
|
421 |
} |
8366025eb
|
422 |
|
2e4b7fcd9
|
423 424 |
static void __mnt_unmake_readonly(struct vfsmount *mnt) { |
99b7db7b8
|
425 |
br_write_lock(vfsmount_lock); |
2e4b7fcd9
|
426 |
mnt->mnt_flags &= ~MNT_READONLY; |
99b7db7b8
|
427 |
br_write_unlock(vfsmount_lock); |
2e4b7fcd9
|
428 |
} |
9d412a43c
|
429 |
static void free_vfsmnt(struct vfsmount *mnt) |
1da177e4c
|
430 431 |
{ kfree(mnt->mnt_devname); |
73cd49ecd
|
432 |
mnt_free_id(mnt); |
d3ef3d735
|
433 |
#ifdef CONFIG_SMP |
b3e19d924
|
434 |
free_percpu(mnt->mnt_pcp); |
d3ef3d735
|
435 |
#endif |
1da177e4c
|
436 437 438 439 |
kmem_cache_free(mnt_cache, mnt); } /* |
a05964f39
|
440 441 |
* find the first or last mount at @dentry on vfsmount @mnt depending on * @dir. If @dir is set return the first mount else return the last mount. |
99b7db7b8
|
442 |
* vfsmount_lock must be held for read or write. |
1da177e4c
|
443 |
*/ |
a05964f39
|
444 445 |
struct vfsmount *__lookup_mnt(struct vfsmount *mnt, struct dentry *dentry, int dir) |
1da177e4c
|
446 |
{ |
b58fed8b1
|
447 448 |
struct list_head *head = mount_hashtable + hash(mnt, dentry); struct list_head *tmp = head; |
1da177e4c
|
449 |
struct vfsmount *p, *found = NULL; |
1da177e4c
|
450 |
for (;;) { |
a05964f39
|
451 |
tmp = dir ? tmp->next : tmp->prev; |
1da177e4c
|
452 453 454 455 456 |
p = NULL; if (tmp == head) break; p = list_entry(tmp, struct vfsmount, mnt_hash); if (p->mnt_parent == mnt && p->mnt_mountpoint == dentry) { |
a05964f39
|
457 |
found = p; |
1da177e4c
|
458 459 460 |
break; } } |
1da177e4c
|
461 462 |
return found; } |
a05964f39
|
463 464 465 466 |
/* * lookup_mnt increments the ref count before returning * the vfsmount struct. */ |
1c755af4d
|
467 |
struct vfsmount *lookup_mnt(struct path *path) |
a05964f39
|
468 469 |
{ struct vfsmount *child_mnt; |
99b7db7b8
|
470 471 |
br_read_lock(vfsmount_lock); |
1c755af4d
|
472 |
if ((child_mnt = __lookup_mnt(path->mnt, path->dentry, 1))) |
a05964f39
|
473 |
mntget(child_mnt); |
99b7db7b8
|
474 |
br_read_unlock(vfsmount_lock); |
a05964f39
|
475 476 |
return child_mnt; } |
1da177e4c
|
477 478 |
static inline int check_mnt(struct vfsmount *mnt) { |
6b3286ed1
|
479 |
return mnt->mnt_ns == current->nsproxy->mnt_ns; |
1da177e4c
|
480 |
} |
99b7db7b8
|
481 482 483 |
/* * vfsmount lock must be held for write */ |
6b3286ed1
|
484 |
static void touch_mnt_namespace(struct mnt_namespace *ns) |
5addc5dd8
|
485 486 487 488 489 490 |
{ if (ns) { ns->event = ++event; wake_up_interruptible(&ns->poll); } } |
99b7db7b8
|
491 492 493 |
/* * vfsmount lock must be held for write */ |
6b3286ed1
|
494 |
static void __touch_mnt_namespace(struct mnt_namespace *ns) |
5addc5dd8
|
495 496 497 498 499 500 |
{ if (ns && ns->event != event) { ns->event = event; wake_up_interruptible(&ns->poll); } } |
99b7db7b8
|
501 |
/* |
5f57cbcc0
|
502 503 504 |
* Clear dentry's mounted state if it has no remaining mounts. * vfsmount_lock must be held for write. */ |
aa0a4cf0a
|
505 |
static void dentry_reset_mounted(struct dentry *dentry) |
5f57cbcc0
|
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 |
{ unsigned u; for (u = 0; u < HASH_SIZE; u++) { struct vfsmount *p; list_for_each_entry(p, &mount_hashtable[u], mnt_hash) { if (p->mnt_mountpoint == dentry) return; } } spin_lock(&dentry->d_lock); dentry->d_flags &= ~DCACHE_MOUNTED; spin_unlock(&dentry->d_lock); } /* |
99b7db7b8
|
523 524 |
* vfsmount lock must be held for write */ |
1a3906895
|
525 |
static void detach_mnt(struct vfsmount *mnt, struct path *old_path) |
1da177e4c
|
526 |
{ |
1a3906895
|
527 528 |
old_path->dentry = mnt->mnt_mountpoint; old_path->mnt = mnt->mnt_parent; |
1da177e4c
|
529 530 531 532 |
mnt->mnt_parent = mnt; mnt->mnt_mountpoint = mnt->mnt_root; list_del_init(&mnt->mnt_child); list_del_init(&mnt->mnt_hash); |
aa0a4cf0a
|
533 |
dentry_reset_mounted(old_path->dentry); |
1da177e4c
|
534 |
} |
99b7db7b8
|
535 536 537 |
/* * vfsmount lock must be held for write */ |
b90fa9ae8
|
538 539 540 541 542 |
void mnt_set_mountpoint(struct vfsmount *mnt, struct dentry *dentry, struct vfsmount *child_mnt) { child_mnt->mnt_parent = mntget(mnt); child_mnt->mnt_mountpoint = dget(dentry); |
5f57cbcc0
|
543 544 545 |
spin_lock(&dentry->d_lock); dentry->d_flags |= DCACHE_MOUNTED; spin_unlock(&dentry->d_lock); |
b90fa9ae8
|
546 |
} |
99b7db7b8
|
547 548 549 |
/* * vfsmount lock must be held for write */ |
1a3906895
|
550 |
static void attach_mnt(struct vfsmount *mnt, struct path *path) |
1da177e4c
|
551 |
{ |
1a3906895
|
552 |
mnt_set_mountpoint(path->mnt, path->dentry, mnt); |
b90fa9ae8
|
553 |
list_add_tail(&mnt->mnt_hash, mount_hashtable + |
1a3906895
|
554 555 |
hash(path->mnt, path->dentry)); list_add_tail(&mnt->mnt_child, &path->mnt->mnt_mounts); |
b90fa9ae8
|
556 |
} |
7e3d0eb0b
|
557 558 559 560 561 562 563 564 565 566 567 568 569 570 |
static inline void __mnt_make_longterm(struct vfsmount *mnt) { #ifdef CONFIG_SMP atomic_inc(&mnt->mnt_longterm); #endif } /* needs vfsmount lock for write */ static inline void __mnt_make_shortterm(struct vfsmount *mnt) { #ifdef CONFIG_SMP atomic_dec(&mnt->mnt_longterm); #endif } |
b90fa9ae8
|
571 |
/* |
99b7db7b8
|
572 |
* vfsmount lock must be held for write |
b90fa9ae8
|
573 574 575 576 577 578 |
*/ static void commit_tree(struct vfsmount *mnt) { struct vfsmount *parent = mnt->mnt_parent; struct vfsmount *m; LIST_HEAD(head); |
6b3286ed1
|
579 |
struct mnt_namespace *n = parent->mnt_ns; |
b90fa9ae8
|
580 581 582 583 |
BUG_ON(parent == mnt); list_add_tail(&head, &mnt->mnt_list); |
f03c65993
|
584 |
list_for_each_entry(m, &head, mnt_list) { |
6b3286ed1
|
585 |
m->mnt_ns = n; |
7e3d0eb0b
|
586 |
__mnt_make_longterm(m); |
f03c65993
|
587 |
} |
b90fa9ae8
|
588 589 590 591 592 |
list_splice(&head, n->list.prev); list_add_tail(&mnt->mnt_hash, mount_hashtable + hash(parent, mnt->mnt_mountpoint)); list_add_tail(&mnt->mnt_child, &parent->mnt_mounts); |
6b3286ed1
|
593 |
touch_mnt_namespace(n); |
1da177e4c
|
594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 |
} static struct vfsmount *next_mnt(struct vfsmount *p, struct vfsmount *root) { struct list_head *next = p->mnt_mounts.next; if (next == &p->mnt_mounts) { while (1) { if (p == root) return NULL; next = p->mnt_child.next; if (next != &p->mnt_parent->mnt_mounts) break; p = p->mnt_parent; } } return list_entry(next, struct vfsmount, mnt_child); } |
9676f0c63
|
611 612 613 614 615 616 617 618 619 |
static struct vfsmount *skip_mnt_tree(struct vfsmount *p) { struct list_head *prev = p->mnt_mounts.prev; while (prev != &p->mnt_mounts) { p = list_entry(prev, struct vfsmount, mnt_child); prev = p->mnt_mounts.prev; } return p; } |
9d412a43c
|
620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 |
struct vfsmount * vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data) { struct vfsmount *mnt; struct dentry *root; if (!type) return ERR_PTR(-ENODEV); mnt = alloc_vfsmnt(name); if (!mnt) return ERR_PTR(-ENOMEM); if (flags & MS_KERNMOUNT) mnt->mnt_flags = MNT_INTERNAL; root = mount_fs(type, flags, name, data); if (IS_ERR(root)) { free_vfsmnt(mnt); return ERR_CAST(root); } mnt->mnt_root = root; mnt->mnt_sb = root->d_sb; mnt->mnt_mountpoint = mnt->mnt_root; mnt->mnt_parent = mnt; return mnt; } EXPORT_SYMBOL_GPL(vfs_kern_mount); |
36341f645
|
649 650 |
static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root, int flag) |
1da177e4c
|
651 652 653 654 655 |
{ struct super_block *sb = old->mnt_sb; struct vfsmount *mnt = alloc_vfsmnt(old->mnt_devname); if (mnt) { |
719f5d7f0
|
656 657 658 659 660 661 662 663 664 665 |
if (flag & (CL_SLAVE | CL_PRIVATE)) mnt->mnt_group_id = 0; /* not a peer of original */ else mnt->mnt_group_id = old->mnt_group_id; if ((flag & CL_MAKE_SHARED) && !mnt->mnt_group_id) { int err = mnt_alloc_group_id(mnt); if (err) goto out_free; } |
be1a16a0a
|
666 |
mnt->mnt_flags = old->mnt_flags & ~MNT_WRITE_HOLD; |
1da177e4c
|
667 668 669 670 671 |
atomic_inc(&sb->s_active); mnt->mnt_sb = sb; mnt->mnt_root = dget(root); mnt->mnt_mountpoint = mnt->mnt_root; mnt->mnt_parent = mnt; |
b90fa9ae8
|
672 |
|
5afe00221
|
673 674 675 676 |
if (flag & CL_SLAVE) { list_add(&mnt->mnt_slave, &old->mnt_slave_list); mnt->mnt_master = old; CLEAR_MNT_SHARED(mnt); |
8aec08094
|
677 |
} else if (!(flag & CL_PRIVATE)) { |
796a6b521
|
678 |
if ((flag & CL_MAKE_SHARED) || IS_MNT_SHARED(old)) |
5afe00221
|
679 680 681 682 683 |
list_add(&mnt->mnt_share, &old->mnt_share); if (IS_MNT_SLAVE(old)) list_add(&mnt->mnt_slave, &old->mnt_slave); mnt->mnt_master = old->mnt_master; } |
b90fa9ae8
|
684 685 |
if (flag & CL_MAKE_SHARED) set_mnt_shared(mnt); |
1da177e4c
|
686 687 688 |
/* stick the duplicate mount on the same expiry list * as the original if that was on one */ |
36341f645
|
689 |
if (flag & CL_EXPIRE) { |
36341f645
|
690 691 |
if (!list_empty(&old->mnt_expire)) list_add(&mnt->mnt_expire, &old->mnt_expire); |
36341f645
|
692 |
} |
1da177e4c
|
693 694 |
} return mnt; |
719f5d7f0
|
695 696 697 698 |
out_free: free_vfsmnt(mnt); return NULL; |
1da177e4c
|
699 |
} |
b3e19d924
|
700 |
static inline void mntfree(struct vfsmount *mnt) |
1da177e4c
|
701 702 |
{ struct super_block *sb = mnt->mnt_sb; |
b3e19d924
|
703 |
|
3d733633a
|
704 |
/* |
3d733633a
|
705 706 707 708 709 |
* This probably indicates that somebody messed * up a mnt_want/drop_write() pair. If this * happens, the filesystem was probably unable * to make r/w->r/o transitions. */ |
d3ef3d735
|
710 |
/* |
b3e19d924
|
711 712 |
* The locking used to deal with mnt_count decrement provides barriers, * so mnt_get_writers() below is safe. |
d3ef3d735
|
713 |
*/ |
c6653a838
|
714 |
WARN_ON(mnt_get_writers(mnt)); |
ca9c726ee
|
715 |
fsnotify_vfsmount_delete(mnt); |
1da177e4c
|
716 717 718 719 |
dput(mnt->mnt_root); free_vfsmnt(mnt); deactivate_super(sb); } |
f03c65993
|
720 |
static void mntput_no_expire(struct vfsmount *mnt) |
b3e19d924
|
721 |
{ |
b3e19d924
|
722 |
put_again: |
f03c65993
|
723 724 725 |
#ifdef CONFIG_SMP br_read_lock(vfsmount_lock); if (likely(atomic_read(&mnt->mnt_longterm))) { |
aa9c0e07b
|
726 |
mnt_add_count(mnt, -1); |
b3e19d924
|
727 |
br_read_unlock(vfsmount_lock); |
f03c65993
|
728 |
return; |
b3e19d924
|
729 |
} |
f03c65993
|
730 |
br_read_unlock(vfsmount_lock); |
b3e19d924
|
731 |
|
99b7db7b8
|
732 |
br_write_lock(vfsmount_lock); |
aa9c0e07b
|
733 |
mnt_add_count(mnt, -1); |
b3e19d924
|
734 |
if (mnt_get_count(mnt)) { |
99b7db7b8
|
735 736 737 |
br_write_unlock(vfsmount_lock); return; } |
b3e19d924
|
738 |
#else |
aa9c0e07b
|
739 |
mnt_add_count(mnt, -1); |
b3e19d924
|
740 |
if (likely(mnt_get_count(mnt))) |
99b7db7b8
|
741 |
return; |
b3e19d924
|
742 |
br_write_lock(vfsmount_lock); |
f03c65993
|
743 |
#endif |
b3e19d924
|
744 745 746 747 748 749 |
if (unlikely(mnt->mnt_pinned)) { mnt_add_count(mnt, mnt->mnt_pinned + 1); mnt->mnt_pinned = 0; br_write_unlock(vfsmount_lock); acct_auto_close_mnt(mnt); goto put_again; |
7b7b1ace2
|
750 |
} |
99b7db7b8
|
751 |
br_write_unlock(vfsmount_lock); |
b3e19d924
|
752 753 |
mntfree(mnt); } |
b3e19d924
|
754 755 756 757 758 759 760 |
void mntput(struct vfsmount *mnt) { if (mnt) { /* avoid cacheline pingpong, hope gcc doesn't get "smart" */ if (unlikely(mnt->mnt_expiry_mark)) mnt->mnt_expiry_mark = 0; |
f03c65993
|
761 |
mntput_no_expire(mnt); |
b3e19d924
|
762 763 764 765 766 767 768 |
} } EXPORT_SYMBOL(mntput); struct vfsmount *mntget(struct vfsmount *mnt) { if (mnt) |
aa9c0e07b
|
769 |
mnt_add_count(mnt, 1); |
b3e19d924
|
770 771 772 |
return mnt; } EXPORT_SYMBOL(mntget); |
7b7b1ace2
|
773 774 |
void mnt_pin(struct vfsmount *mnt) { |
99b7db7b8
|
775 |
br_write_lock(vfsmount_lock); |
7b7b1ace2
|
776 |
mnt->mnt_pinned++; |
99b7db7b8
|
777 |
br_write_unlock(vfsmount_lock); |
7b7b1ace2
|
778 |
} |
7b7b1ace2
|
779 780 781 782 |
EXPORT_SYMBOL(mnt_pin); void mnt_unpin(struct vfsmount *mnt) { |
99b7db7b8
|
783 |
br_write_lock(vfsmount_lock); |
7b7b1ace2
|
784 |
if (mnt->mnt_pinned) { |
aa9c0e07b
|
785 |
mnt_add_count(mnt, 1); |
7b7b1ace2
|
786 787 |
mnt->mnt_pinned--; } |
99b7db7b8
|
788 |
br_write_unlock(vfsmount_lock); |
7b7b1ace2
|
789 |
} |
7b7b1ace2
|
790 |
EXPORT_SYMBOL(mnt_unpin); |
1da177e4c
|
791 |
|
b3b304a23
|
792 793 794 795 796 797 798 799 800 801 802 803 804 805 |
static inline void mangle(struct seq_file *m, const char *s) { seq_escape(m, s, " \t \\"); } /* * Simple .show_options callback for filesystems which don't want to * implement more complex mount option showing. * * See also save_mount_options(). */ int generic_show_options(struct seq_file *m, struct vfsmount *mnt) { |
2a32cebd6
|
806 807 808 809 |
const char *options; rcu_read_lock(); options = rcu_dereference(mnt->mnt_sb->s_options); |
b3b304a23
|
810 811 812 813 814 |
if (options != NULL && options[0]) { seq_putc(m, ','); mangle(m, options); } |
2a32cebd6
|
815 |
rcu_read_unlock(); |
b3b304a23
|
816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 |
return 0; } EXPORT_SYMBOL(generic_show_options); /* * If filesystem uses generic_show_options(), this function should be * called from the fill_super() callback. * * The .remount_fs callback usually needs to be handled in a special * way, to make sure, that previous options are not overwritten if the * remount fails. * * Also note, that if the filesystem's .remount_fs function doesn't * reset all options to their default value, but changes only newly * given options, then the displayed options will not reflect reality * any more. */ void save_mount_options(struct super_block *sb, char *options) { |
2a32cebd6
|
836 837 |
BUG_ON(sb->s_options); rcu_assign_pointer(sb->s_options, kstrdup(options, GFP_KERNEL)); |
b3b304a23
|
838 839 |
} EXPORT_SYMBOL(save_mount_options); |
2a32cebd6
|
840 841 842 843 844 845 846 847 848 849 |
void replace_mount_options(struct super_block *sb, char *options) { char *old = sb->s_options; rcu_assign_pointer(sb->s_options, options); if (old) { synchronize_rcu(); kfree(old); } } EXPORT_SYMBOL(replace_mount_options); |
a1a2c409b
|
850 |
#ifdef CONFIG_PROC_FS |
1da177e4c
|
851 852 853 |
/* iterator */ static void *m_start(struct seq_file *m, loff_t *pos) { |
a1a2c409b
|
854 |
struct proc_mounts *p = m->private; |
1da177e4c
|
855 |
|
390c68436
|
856 |
down_read(&namespace_sem); |
a1a2c409b
|
857 |
return seq_list_start(&p->ns->list, *pos); |
1da177e4c
|
858 859 860 861 |
} static void *m_next(struct seq_file *m, void *v, loff_t *pos) { |
a1a2c409b
|
862 |
struct proc_mounts *p = m->private; |
b0765fb85
|
863 |
|
a1a2c409b
|
864 |
return seq_list_next(v, &p->ns->list, pos); |
1da177e4c
|
865 866 867 868 |
} static void m_stop(struct seq_file *m, void *v) { |
390c68436
|
869 |
up_read(&namespace_sem); |
1da177e4c
|
870 |
} |
9f5596af4
|
871 872 873 874 |
int mnt_had_events(struct proc_mounts *p) { struct mnt_namespace *ns = p->ns; int res = 0; |
99b7db7b8
|
875 |
br_read_lock(vfsmount_lock); |
f15146380
|
876 877 |
if (p->m.poll_event != ns->event) { p->m.poll_event = ns->event; |
9f5596af4
|
878 879 |
res = 1; } |
99b7db7b8
|
880 |
br_read_unlock(vfsmount_lock); |
9f5596af4
|
881 882 883 |
return res; } |
2d4d4864a
|
884 885 886 887 |
struct proc_fs_info { int flag; const char *str; }; |
2069f4578
|
888 |
static int show_sb_opts(struct seq_file *m, struct super_block *sb) |
1da177e4c
|
889 |
{ |
2d4d4864a
|
890 |
static const struct proc_fs_info fs_info[] = { |
1da177e4c
|
891 892 893 |
{ MS_SYNCHRONOUS, ",sync" }, { MS_DIRSYNC, ",dirsync" }, { MS_MANDLOCK, ",mand" }, |
1da177e4c
|
894 895 |
{ 0, NULL } }; |
2d4d4864a
|
896 897 898 899 900 901 |
const struct proc_fs_info *fs_infop; for (fs_infop = fs_info; fs_infop->flag; fs_infop++) { if (sb->s_flags & fs_infop->flag) seq_puts(m, fs_infop->str); } |
2069f4578
|
902 903 |
return security_sb_show_options(m, sb); |
2d4d4864a
|
904 905 906 907 908 |
} static void show_mnt_opts(struct seq_file *m, struct vfsmount *mnt) { static const struct proc_fs_info mnt_info[] = { |
1da177e4c
|
909 910 911 |
{ MNT_NOSUID, ",nosuid" }, { MNT_NODEV, ",nodev" }, { MNT_NOEXEC, ",noexec" }, |
fc33a7bb9
|
912 913 |
{ MNT_NOATIME, ",noatime" }, { MNT_NODIRATIME, ",nodiratime" }, |
47ae32d6a
|
914 |
{ MNT_RELATIME, ",relatime" }, |
1da177e4c
|
915 916 |
{ 0, NULL } }; |
2d4d4864a
|
917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 |
const struct proc_fs_info *fs_infop; for (fs_infop = mnt_info; fs_infop->flag; fs_infop++) { if (mnt->mnt_flags & fs_infop->flag) seq_puts(m, fs_infop->str); } } static void show_type(struct seq_file *m, struct super_block *sb) { mangle(m, sb->s_type->name); if (sb->s_subtype && sb->s_subtype[0]) { seq_putc(m, '.'); mangle(m, sb->s_subtype); } } static int show_vfsmnt(struct seq_file *m, void *v) { struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list); int err = 0; |
c32c2f63a
|
938 |
struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt }; |
1da177e4c
|
939 |
|
c7f404b40
|
940 941 942 943 944 945 946 |
if (mnt->mnt_sb->s_op->show_devname) { err = mnt->mnt_sb->s_op->show_devname(m, mnt); if (err) goto out; } else { mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none"); } |
1da177e4c
|
947 |
seq_putc(m, ' '); |
c32c2f63a
|
948 949 |
seq_path(m, &mnt_path, " \t \\"); |
1da177e4c
|
950 |
seq_putc(m, ' '); |
2d4d4864a
|
951 |
show_type(m, mnt->mnt_sb); |
2e4b7fcd9
|
952 |
seq_puts(m, __mnt_is_readonly(mnt) ? " ro" : " rw"); |
2069f4578
|
953 954 955 |
err = show_sb_opts(m, mnt->mnt_sb); if (err) goto out; |
2d4d4864a
|
956 |
show_mnt_opts(m, mnt); |
1da177e4c
|
957 958 959 960 |
if (mnt->mnt_sb->s_op->show_options) err = mnt->mnt_sb->s_op->show_options(m, mnt); seq_puts(m, " 0 0 "); |
2069f4578
|
961 |
out: |
1da177e4c
|
962 963 |
return err; } |
a1a2c409b
|
964 |
const struct seq_operations mounts_op = { |
1da177e4c
|
965 966 967 968 969 |
.start = m_start, .next = m_next, .stop = m_stop, .show = show_vfsmnt }; |
2d4d4864a
|
970 971 972 973 974 975 976 977 978 979 980 |
static int show_mountinfo(struct seq_file *m, void *v) { struct proc_mounts *p = m->private; struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list); struct super_block *sb = mnt->mnt_sb; struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt }; struct path root = p->root; int err = 0; seq_printf(m, "%i %i %u:%u ", mnt->mnt_id, mnt->mnt_parent->mnt_id, MAJOR(sb->s_dev), MINOR(sb->s_dev)); |
c7f404b40
|
981 982 983 984 985 986 987 |
if (sb->s_op->show_path) err = sb->s_op->show_path(m, mnt); else seq_dentry(m, mnt->mnt_root, " \t \\"); if (err) goto out; |
2d4d4864a
|
988 |
seq_putc(m, ' '); |
02125a826
|
989 990 991 992 993 994 |
/* mountpoints outside of chroot jail will give SEQ_SKIP on this */ err = seq_path_root(m, &mnt_path, &root, " \t \\"); if (err) goto out; |
2d4d4864a
|
995 996 997 998 999 1000 |
seq_puts(m, mnt->mnt_flags & MNT_READONLY ? " ro" : " rw"); show_mnt_opts(m, mnt); /* Tagged fields ("foo:X" or "bar") */ if (IS_MNT_SHARED(mnt)) seq_printf(m, " shared:%i", mnt->mnt_group_id); |
97e7e0f71
|
1001 1002 1003 1004 1005 1006 1007 |
if (IS_MNT_SLAVE(mnt)) { int master = mnt->mnt_master->mnt_group_id; int dom = get_dominating_id(mnt, &p->root); seq_printf(m, " master:%i", master); if (dom && dom != master) seq_printf(m, " propagate_from:%i", dom); } |
2d4d4864a
|
1008 1009 1010 1011 1012 1013 1014 |
if (IS_MNT_UNBINDABLE(mnt)) seq_puts(m, " unbindable"); /* Filesystem specific data */ seq_puts(m, " - "); show_type(m, sb); seq_putc(m, ' '); |
c7f404b40
|
1015 1016 1017 1018 1019 1020 |
if (sb->s_op->show_devname) err = sb->s_op->show_devname(m, mnt); else mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none"); if (err) goto out; |
2d4d4864a
|
1021 |
seq_puts(m, sb->s_flags & MS_RDONLY ? " ro" : " rw"); |
2069f4578
|
1022 1023 1024 |
err = show_sb_opts(m, sb); if (err) goto out; |
2d4d4864a
|
1025 1026 1027 1028 |
if (sb->s_op->show_options) err = sb->s_op->show_options(m, mnt); seq_putc(m, ' '); |
2069f4578
|
1029 |
out: |
2d4d4864a
|
1030 1031 1032 1033 1034 1035 1036 1037 1038 |
return err; } const struct seq_operations mountinfo_op = { .start = m_start, .next = m_next, .stop = m_stop, .show = show_mountinfo, }; |
b4629fe2f
|
1039 1040 |
static int show_vfsstat(struct seq_file *m, void *v) { |
b0765fb85
|
1041 |
struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list); |
c32c2f63a
|
1042 |
struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt }; |
b4629fe2f
|
1043 1044 1045 |
int err = 0; /* device */ |
c7f404b40
|
1046 |
if (mnt->mnt_sb->s_op->show_devname) { |
a877ee03a
|
1047 |
seq_puts(m, "device "); |
c7f404b40
|
1048 1049 1050 1051 1052 1053 1054 1055 |
err = mnt->mnt_sb->s_op->show_devname(m, mnt); } else { if (mnt->mnt_devname) { seq_puts(m, "device "); mangle(m, mnt->mnt_devname); } else seq_puts(m, "no device"); } |
b4629fe2f
|
1056 1057 1058 |
/* mount point */ seq_puts(m, " mounted on "); |
c32c2f63a
|
1059 1060 |
seq_path(m, &mnt_path, " \t \\"); |
b4629fe2f
|
1061 1062 1063 1064 |
seq_putc(m, ' '); /* file system type */ seq_puts(m, "with fstype "); |
2d4d4864a
|
1065 |
show_type(m, mnt->mnt_sb); |
b4629fe2f
|
1066 1067 1068 1069 |
/* optional statistics */ if (mnt->mnt_sb->s_op->show_stats) { seq_putc(m, ' '); |
c7f404b40
|
1070 1071 |
if (!err) err = mnt->mnt_sb->s_op->show_stats(m, mnt); |
b4629fe2f
|
1072 1073 1074 1075 1076 1077 |
} seq_putc(m, ' '); return err; } |
a1a2c409b
|
1078 |
const struct seq_operations mountstats_op = { |
b4629fe2f
|
1079 1080 1081 1082 1083 |
.start = m_start, .next = m_next, .stop = m_stop, .show = show_vfsstat, }; |
a1a2c409b
|
1084 |
#endif /* CONFIG_PROC_FS */ |
b4629fe2f
|
1085 |
|
1da177e4c
|
1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 |
/** * may_umount_tree - check if a mount tree is busy * @mnt: root of mount tree * * This is called to check if a tree of mounts has any * open files, pwds, chroots or sub mounts that are * busy. */ int may_umount_tree(struct vfsmount *mnt) { |
36341f645
|
1096 1097 1098 |
int actual_refs = 0; int minimum_refs = 0; struct vfsmount *p; |
1da177e4c
|
1099 |
|
b3e19d924
|
1100 1101 |
/* write lock needed for mnt_get_count */ br_write_lock(vfsmount_lock); |
36341f645
|
1102 |
for (p = mnt; p; p = next_mnt(p, mnt)) { |
b3e19d924
|
1103 |
actual_refs += mnt_get_count(p); |
1da177e4c
|
1104 |
minimum_refs += 2; |
1da177e4c
|
1105 |
} |
b3e19d924
|
1106 |
br_write_unlock(vfsmount_lock); |
1da177e4c
|
1107 1108 |
if (actual_refs > minimum_refs) |
e3474a8eb
|
1109 |
return 0; |
1da177e4c
|
1110 |
|
e3474a8eb
|
1111 |
return 1; |
1da177e4c
|
1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 |
} EXPORT_SYMBOL(may_umount_tree); /** * may_umount - check if a mount point is busy * @mnt: root of mount * * This is called to check if a mount point has any * open files, pwds, chroots or sub mounts. If the * mount has sub mounts this will return busy * regardless of whether the sub mounts are busy. * * Doesn't take quota and stuff into account. IOW, in some cases it will * give false negatives. The main reason why it's here is that we need * a non-destructive way to look for easily umountable filesystems. */ int may_umount(struct vfsmount *mnt) { |
e3474a8eb
|
1131 |
int ret = 1; |
8ad08d8a0
|
1132 |
down_read(&namespace_sem); |
b3e19d924
|
1133 |
br_write_lock(vfsmount_lock); |
a05964f39
|
1134 |
if (propagate_mount_busy(mnt, 2)) |
e3474a8eb
|
1135 |
ret = 0; |
b3e19d924
|
1136 |
br_write_unlock(vfsmount_lock); |
8ad08d8a0
|
1137 |
up_read(&namespace_sem); |
a05964f39
|
1138 |
return ret; |
1da177e4c
|
1139 1140 1141 |
} EXPORT_SYMBOL(may_umount); |
b90fa9ae8
|
1142 |
void release_mounts(struct list_head *head) |
70fbcdf4d
|
1143 1144 |
{ struct vfsmount *mnt; |
bf066c7db
|
1145 |
while (!list_empty(head)) { |
b5e618181
|
1146 |
mnt = list_first_entry(head, struct vfsmount, mnt_hash); |
70fbcdf4d
|
1147 |
list_del_init(&mnt->mnt_hash); |
b2dba1af3
|
1148 |
if (mnt_has_parent(mnt)) { |
70fbcdf4d
|
1149 1150 |
struct dentry *dentry; struct vfsmount *m; |
99b7db7b8
|
1151 1152 |
br_write_lock(vfsmount_lock); |
70fbcdf4d
|
1153 1154 1155 1156 |
dentry = mnt->mnt_mountpoint; m = mnt->mnt_parent; mnt->mnt_mountpoint = mnt->mnt_root; mnt->mnt_parent = mnt; |
7c4b93d82
|
1157 |
m->mnt_ghosts--; |
99b7db7b8
|
1158 |
br_write_unlock(vfsmount_lock); |
70fbcdf4d
|
1159 1160 1161 |
dput(dentry); mntput(m); } |
f03c65993
|
1162 |
mntput(mnt); |
70fbcdf4d
|
1163 1164 |
} } |
99b7db7b8
|
1165 1166 1167 1168 |
/* * vfsmount lock must be held for write * namespace_sem must be held for write */ |
a05964f39
|
1169 |
void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill) |
1da177e4c
|
1170 |
{ |
7b8a53fd8
|
1171 |
LIST_HEAD(tmp_list); |
1da177e4c
|
1172 |
struct vfsmount *p; |
1da177e4c
|
1173 |
|
1bfba4e8e
|
1174 |
for (p = mnt; p; p = next_mnt(p, mnt)) |
7b8a53fd8
|
1175 |
list_move(&p->mnt_hash, &tmp_list); |
1da177e4c
|
1176 |
|
a05964f39
|
1177 |
if (propagate) |
7b8a53fd8
|
1178 |
propagate_umount(&tmp_list); |
a05964f39
|
1179 |
|
7b8a53fd8
|
1180 |
list_for_each_entry(p, &tmp_list, mnt_hash) { |
70fbcdf4d
|
1181 1182 |
list_del_init(&p->mnt_expire); list_del_init(&p->mnt_list); |
6b3286ed1
|
1183 1184 |
__touch_mnt_namespace(p->mnt_ns); p->mnt_ns = NULL; |
7e3d0eb0b
|
1185 |
__mnt_make_shortterm(p); |
70fbcdf4d
|
1186 |
list_del_init(&p->mnt_child); |
b2dba1af3
|
1187 |
if (mnt_has_parent(p)) { |
7c4b93d82
|
1188 |
p->mnt_parent->mnt_ghosts++; |
aa0a4cf0a
|
1189 |
dentry_reset_mounted(p->mnt_mountpoint); |
7c4b93d82
|
1190 |
} |
a05964f39
|
1191 |
change_mnt_propagation(p, MS_PRIVATE); |
1da177e4c
|
1192 |
} |
7b8a53fd8
|
1193 |
list_splice(&tmp_list, kill); |
1da177e4c
|
1194 |
} |
c35038bec
|
1195 |
static void shrink_submounts(struct vfsmount *mnt, struct list_head *umounts); |
1da177e4c
|
1196 1197 |
static int do_umount(struct vfsmount *mnt, int flags) { |
b58fed8b1
|
1198 |
struct super_block *sb = mnt->mnt_sb; |
1da177e4c
|
1199 |
int retval; |
70fbcdf4d
|
1200 |
LIST_HEAD(umount_list); |
1da177e4c
|
1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 |
retval = security_sb_umount(mnt, flags); if (retval) return retval; /* * Allow userspace to request a mountpoint be expired rather than * unmounting unconditionally. Unmount only happens if: * (1) the mark is already set (the mark is cleared by mntput()) * (2) the usage count == 1 [parent vfsmount] + 1 [sys_umount] */ if (flags & MNT_EXPIRE) { |
6ac08c39a
|
1213 |
if (mnt == current->fs->root.mnt || |
1da177e4c
|
1214 1215 |
flags & (MNT_FORCE | MNT_DETACH)) return -EINVAL; |
b3e19d924
|
1216 1217 1218 1219 1220 1221 |
/* * probably don't strictly need the lock here if we examined * all race cases, but it's a slowpath. */ br_write_lock(vfsmount_lock); if (mnt_get_count(mnt) != 2) { |
bf9faa2aa
|
1222 |
br_write_unlock(vfsmount_lock); |
1da177e4c
|
1223 |
return -EBUSY; |
b3e19d924
|
1224 1225 |
} br_write_unlock(vfsmount_lock); |
1da177e4c
|
1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 |
if (!xchg(&mnt->mnt_expiry_mark, 1)) return -EAGAIN; } /* * If we may have to abort operations to get out of this * mount, and they will themselves hold resources we must * allow the fs to do things. In the Unix tradition of * 'Gee thats tricky lets do it in userspace' the umount_begin * might fail to complete on the first run through as other tasks * must return, and the like. Thats for the mount program to worry * about for the moment. */ |
42faad996
|
1240 |
if (flags & MNT_FORCE && sb->s_op->umount_begin) { |
42faad996
|
1241 |
sb->s_op->umount_begin(sb); |
42faad996
|
1242 |
} |
1da177e4c
|
1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 |
/* * No sense to grab the lock for this test, but test itself looks * somewhat bogus. Suggestions for better replacement? * Ho-hum... In principle, we might treat that as umount + switch * to rootfs. GC would eventually take care of the old vfsmount. * Actually it makes sense, especially if rootfs would contain a * /reboot - static binary that would close all descriptors and * call reboot(9). Then init(8) could umount root and exec /reboot. */ |
6ac08c39a
|
1253 |
if (mnt == current->fs->root.mnt && !(flags & MNT_DETACH)) { |
1da177e4c
|
1254 1255 1256 1257 1258 |
/* * Special case for "unmounting" root ... * we just try to remount it readonly. */ down_write(&sb->s_umount); |
4aa98cf76
|
1259 |
if (!(sb->s_flags & MS_RDONLY)) |
1da177e4c
|
1260 |
retval = do_remount_sb(sb, MS_RDONLY, NULL, 0); |
1da177e4c
|
1261 1262 1263 |
up_write(&sb->s_umount); return retval; } |
390c68436
|
1264 |
down_write(&namespace_sem); |
99b7db7b8
|
1265 |
br_write_lock(vfsmount_lock); |
5addc5dd8
|
1266 |
event++; |
1da177e4c
|
1267 |
|
c35038bec
|
1268 1269 |
if (!(flags & MNT_DETACH)) shrink_submounts(mnt, &umount_list); |
1da177e4c
|
1270 |
retval = -EBUSY; |
a05964f39
|
1271 |
if (flags & MNT_DETACH || !propagate_mount_busy(mnt, 2)) { |
1da177e4c
|
1272 |
if (!list_empty(&mnt->mnt_list)) |
a05964f39
|
1273 |
umount_tree(mnt, 1, &umount_list); |
1da177e4c
|
1274 1275 |
retval = 0; } |
99b7db7b8
|
1276 |
br_write_unlock(vfsmount_lock); |
390c68436
|
1277 |
up_write(&namespace_sem); |
70fbcdf4d
|
1278 |
release_mounts(&umount_list); |
1da177e4c
|
1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 |
return retval; } /* * Now umount can handle mount points as well as block devices. * This is important for filesystems which use unnamed block devices. * * We now support a flag for forced unmount like the other 'big iron' * unixes. Our API is identical to OSF/1 to avoid making a mess of AMD */ |
bdc480e3b
|
1289 |
SYSCALL_DEFINE2(umount, char __user *, name, int, flags) |
1da177e4c
|
1290 |
{ |
2d8f30380
|
1291 |
struct path path; |
1da177e4c
|
1292 |
int retval; |
db1f05bb8
|
1293 |
int lookup_flags = 0; |
1da177e4c
|
1294 |
|
db1f05bb8
|
1295 1296 1297 1298 1299 1300 1301 |
if (flags & ~(MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW)) return -EINVAL; if (!(flags & UMOUNT_NOFOLLOW)) lookup_flags |= LOOKUP_FOLLOW; retval = user_path_at(AT_FDCWD, name, lookup_flags, &path); |
1da177e4c
|
1302 1303 1304 |
if (retval) goto out; retval = -EINVAL; |
2d8f30380
|
1305 |
if (path.dentry != path.mnt->mnt_root) |
1da177e4c
|
1306 |
goto dput_and_out; |
2d8f30380
|
1307 |
if (!check_mnt(path.mnt)) |
1da177e4c
|
1308 1309 1310 1311 1312 |
goto dput_and_out; retval = -EPERM; if (!capable(CAP_SYS_ADMIN)) goto dput_and_out; |
2d8f30380
|
1313 |
retval = do_umount(path.mnt, flags); |
1da177e4c
|
1314 |
dput_and_out: |
429731b15
|
1315 |
/* we mustn't call path_put() as that would clear mnt_expiry_mark */ |
2d8f30380
|
1316 1317 |
dput(path.dentry); mntput_no_expire(path.mnt); |
1da177e4c
|
1318 1319 1320 1321 1322 1323 1324 |
out: return retval; } #ifdef __ARCH_WANT_SYS_OLDUMOUNT /* |
b58fed8b1
|
1325 |
* The 2.0 compatible umount. No flags. |
1da177e4c
|
1326 |
*/ |
bdc480e3b
|
1327 |
SYSCALL_DEFINE1(oldumount, char __user *, name) |
1da177e4c
|
1328 |
{ |
b58fed8b1
|
1329 |
return sys_umount(name, 0); |
1da177e4c
|
1330 1331 1332 |
} #endif |
2d92ab3c6
|
1333 |
static int mount_is_safe(struct path *path) |
1da177e4c
|
1334 1335 1336 1337 1338 |
{ if (capable(CAP_SYS_ADMIN)) return 0; return -EPERM; #ifdef notyet |
2d92ab3c6
|
1339 |
if (S_ISLNK(path->dentry->d_inode->i_mode)) |
1da177e4c
|
1340 |
return -EPERM; |
2d92ab3c6
|
1341 |
if (path->dentry->d_inode->i_mode & S_ISVTX) { |
da9592ede
|
1342 |
if (current_uid() != path->dentry->d_inode->i_uid) |
1da177e4c
|
1343 1344 |
return -EPERM; } |
2d92ab3c6
|
1345 |
if (inode_permission(path->dentry->d_inode, MAY_WRITE)) |
1da177e4c
|
1346 1347 1348 1349 |
return -EPERM; return 0; #endif } |
b90fa9ae8
|
1350 |
struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry, |
36341f645
|
1351 |
int flag) |
1da177e4c
|
1352 1353 |
{ struct vfsmount *res, *p, *q, *r, *s; |
1a3906895
|
1354 |
struct path path; |
1da177e4c
|
1355 |
|
9676f0c63
|
1356 1357 |
if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(mnt)) return NULL; |
36341f645
|
1358 |
res = q = clone_mnt(mnt, dentry, flag); |
1da177e4c
|
1359 1360 1361 1362 1363 |
if (!q) goto Enomem; q->mnt_mountpoint = mnt->mnt_mountpoint; p = mnt; |
fdadd65fb
|
1364 |
list_for_each_entry(r, &mnt->mnt_mounts, mnt_child) { |
7ec02ef15
|
1365 |
if (!is_subdir(r->mnt_mountpoint, dentry)) |
1da177e4c
|
1366 1367 1368 |
continue; for (s = r; s; s = next_mnt(s, r)) { |
9676f0c63
|
1369 1370 1371 1372 |
if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(s)) { s = skip_mnt_tree(s); continue; } |
1da177e4c
|
1373 1374 1375 1376 1377 |
while (p != s->mnt_parent) { p = p->mnt_parent; q = q->mnt_parent; } p = s; |
1a3906895
|
1378 1379 |
path.mnt = q; path.dentry = p->mnt_mountpoint; |
36341f645
|
1380 |
q = clone_mnt(p, p->mnt_root, flag); |
1da177e4c
|
1381 1382 |
if (!q) goto Enomem; |
99b7db7b8
|
1383 |
br_write_lock(vfsmount_lock); |
1da177e4c
|
1384 |
list_add_tail(&q->mnt_list, &res->mnt_list); |
1a3906895
|
1385 |
attach_mnt(q, &path); |
99b7db7b8
|
1386 |
br_write_unlock(vfsmount_lock); |
1da177e4c
|
1387 1388 1389 |
} } return res; |
b58fed8b1
|
1390 |
Enomem: |
1da177e4c
|
1391 |
if (res) { |
70fbcdf4d
|
1392 |
LIST_HEAD(umount_list); |
99b7db7b8
|
1393 |
br_write_lock(vfsmount_lock); |
a05964f39
|
1394 |
umount_tree(res, 0, &umount_list); |
99b7db7b8
|
1395 |
br_write_unlock(vfsmount_lock); |
70fbcdf4d
|
1396 |
release_mounts(&umount_list); |
1da177e4c
|
1397 1398 1399 |
} return NULL; } |
589ff870e
|
1400 |
struct vfsmount *collect_mounts(struct path *path) |
8aec08094
|
1401 1402 |
{ struct vfsmount *tree; |
1a60a2807
|
1403 |
down_write(&namespace_sem); |
589ff870e
|
1404 |
tree = copy_tree(path->mnt, path->dentry, CL_COPY_ALL | CL_PRIVATE); |
1a60a2807
|
1405 |
up_write(&namespace_sem); |
8aec08094
|
1406 1407 1408 1409 1410 1411 |
return tree; } void drop_collected_mounts(struct vfsmount *mnt) { LIST_HEAD(umount_list); |
1a60a2807
|
1412 |
down_write(&namespace_sem); |
99b7db7b8
|
1413 |
br_write_lock(vfsmount_lock); |
8aec08094
|
1414 |
umount_tree(mnt, 0, &umount_list); |
99b7db7b8
|
1415 |
br_write_unlock(vfsmount_lock); |
1a60a2807
|
1416 |
up_write(&namespace_sem); |
8aec08094
|
1417 1418 |
release_mounts(&umount_list); } |
1f707137b
|
1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 |
int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg, struct vfsmount *root) { struct vfsmount *mnt; int res = f(root, arg); if (res) return res; list_for_each_entry(mnt, &root->mnt_list, mnt_list) { res = f(mnt, arg); if (res) return res; } return 0; } |
719f5d7f0
|
1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 |
static void cleanup_group_ids(struct vfsmount *mnt, struct vfsmount *end) { struct vfsmount *p; for (p = mnt; p != end; p = next_mnt(p, mnt)) { if (p->mnt_group_id && !IS_MNT_SHARED(p)) mnt_release_group_id(p); } } static int invent_group_ids(struct vfsmount *mnt, bool recurse) { struct vfsmount *p; for (p = mnt; p; p = recurse ? next_mnt(p, mnt) : NULL) { if (!p->mnt_group_id && !IS_MNT_SHARED(p)) { int err = mnt_alloc_group_id(p); if (err) { cleanup_group_ids(mnt, p); return err; } } } return 0; } |
b90fa9ae8
|
1459 1460 |
/* * @source_mnt : mount tree to be attached |
214444032
|
1461 1462 1463 1464 |
* @nd : place the mount tree @source_mnt is attached * @parent_nd : if non-null, detach the source_mnt from its parent and * store the parent mount and mountpoint dentry. * (done when source_mnt is moved) |
b90fa9ae8
|
1465 1466 1467 |
* * NOTE: in the table below explains the semantics when a source mount * of a given type is attached to a destination mount of a given type. |
9676f0c63
|
1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 |
* --------------------------------------------------------------------------- * | BIND MOUNT OPERATION | * |************************************************************************** * | source-->| shared | private | slave | unbindable | * | dest | | | | | * | | | | | | | * | v | | | | | * |************************************************************************** * | shared | shared (++) | shared (+) | shared(+++)| invalid | * | | | | | | * |non-shared| shared (+) | private | slave (*) | invalid | * *************************************************************************** |
b90fa9ae8
|
1480 1481 1482 1483 1484 1485 1486 1487 1488 |
* A bind operation clones the source mount and mounts the clone on the * destination mount. * * (++) the cloned mount is propagated to all the mounts in the propagation * tree of the destination mount and the cloned mount is added to * the peer group of the source mount. * (+) the cloned mount is created under the destination mount and is marked * as shared. The cloned mount is added to the peer group of the source * mount. |
5afe00221
|
1489 1490 1491 1492 1493 1494 1495 |
* (+++) the mount is propagated to all the mounts in the propagation tree * of the destination mount and the cloned mount is made slave * of the same master as that of the source mount. The cloned mount * is marked as 'shared and slave'. * (*) the cloned mount is made a slave of the same master as that of the * source mount. * |
9676f0c63
|
1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 |
* --------------------------------------------------------------------------- * | MOVE MOUNT OPERATION | * |************************************************************************** * | source-->| shared | private | slave | unbindable | * | dest | | | | | * | | | | | | | * | v | | | | | * |************************************************************************** * | shared | shared (+) | shared (+) | shared(+++) | invalid | * | | | | | | * |non-shared| shared (+*) | private | slave (*) | unbindable | * *************************************************************************** |
5afe00221
|
1508 1509 1510 |
* * (+) the mount is moved to the destination. And is then propagated to * all the mounts in the propagation tree of the destination mount. |
214444032
|
1511 |
* (+*) the mount is moved to the destination. |
5afe00221
|
1512 1513 1514 1515 |
* (+++) the mount is moved to the destination and is then propagated to * all the mounts belonging to the destination mount's propagation tree. * the mount is marked as 'shared and slave'. * (*) the mount continues to be a slave at the new location. |
b90fa9ae8
|
1516 1517 1518 1519 1520 1521 1522 |
* * if the source mount is a tree, the operations explained above is * applied to each mount in the tree. * Must be called without spinlocks held, since this function can sleep * in allocations. */ static int attach_recursive_mnt(struct vfsmount *source_mnt, |
1a3906895
|
1523 |
struct path *path, struct path *parent_path) |
b90fa9ae8
|
1524 1525 |
{ LIST_HEAD(tree_list); |
1a3906895
|
1526 1527 |
struct vfsmount *dest_mnt = path->mnt; struct dentry *dest_dentry = path->dentry; |
b90fa9ae8
|
1528 |
struct vfsmount *child, *p; |
719f5d7f0
|
1529 |
int err; |
b90fa9ae8
|
1530 |
|
719f5d7f0
|
1531 1532 1533 1534 1535 1536 1537 1538 |
if (IS_MNT_SHARED(dest_mnt)) { err = invent_group_ids(source_mnt, true); if (err) goto out; } err = propagate_mnt(dest_mnt, dest_dentry, source_mnt, &tree_list); if (err) goto out_cleanup_ids; |
b90fa9ae8
|
1539 |
|
99b7db7b8
|
1540 |
br_write_lock(vfsmount_lock); |
df1a1ad29
|
1541 |
|
b90fa9ae8
|
1542 1543 1544 1545 |
if (IS_MNT_SHARED(dest_mnt)) { for (p = source_mnt; p; p = next_mnt(p, source_mnt)) set_mnt_shared(p); } |
1a3906895
|
1546 1547 1548 |
if (parent_path) { detach_mnt(source_mnt, parent_path); attach_mnt(source_mnt, path); |
e5d67f071
|
1549 |
touch_mnt_namespace(parent_path->mnt->mnt_ns); |
214444032
|
1550 1551 1552 1553 |
} else { mnt_set_mountpoint(dest_mnt, dest_dentry, source_mnt); commit_tree(source_mnt); } |
b90fa9ae8
|
1554 1555 1556 1557 1558 |
list_for_each_entry_safe(child, p, &tree_list, mnt_hash) { list_del_init(&child->mnt_hash); commit_tree(child); } |
99b7db7b8
|
1559 |
br_write_unlock(vfsmount_lock); |
b90fa9ae8
|
1560 |
return 0; |
719f5d7f0
|
1561 1562 1563 1564 1565 1566 |
out_cleanup_ids: if (IS_MNT_SHARED(dest_mnt)) cleanup_group_ids(source_mnt, NULL); out: return err; |
b90fa9ae8
|
1567 |
} |
b12cea919
|
1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 |
static int lock_mount(struct path *path) { struct vfsmount *mnt; retry: mutex_lock(&path->dentry->d_inode->i_mutex); if (unlikely(cant_mount(path->dentry))) { mutex_unlock(&path->dentry->d_inode->i_mutex); return -ENOENT; } down_write(&namespace_sem); mnt = lookup_mnt(path); if (likely(!mnt)) return 0; up_write(&namespace_sem); mutex_unlock(&path->dentry->d_inode->i_mutex); path_put(path); path->mnt = mnt; path->dentry = dget(mnt->mnt_root); goto retry; } static void unlock_mount(struct path *path) { up_write(&namespace_sem); mutex_unlock(&path->dentry->d_inode->i_mutex); } |
8c3ee42e8
|
1594 |
static int graft_tree(struct vfsmount *mnt, struct path *path) |
1da177e4c
|
1595 |
{ |
1da177e4c
|
1596 1597 |
if (mnt->mnt_sb->s_flags & MS_NOUSER) return -EINVAL; |
8c3ee42e8
|
1598 |
if (S_ISDIR(path->dentry->d_inode->i_mode) != |
1da177e4c
|
1599 1600 |
S_ISDIR(mnt->mnt_root->d_inode->i_mode)) return -ENOTDIR; |
b12cea919
|
1601 1602 |
if (d_unlinked(path->dentry)) return -ENOENT; |
1da177e4c
|
1603 |
|
b12cea919
|
1604 |
return attach_recursive_mnt(mnt, path, NULL); |
1da177e4c
|
1605 1606 1607 |
} /* |
7a2e8a8fa
|
1608 1609 1610 1611 1612 |
* Sanity check the flags to change_mnt_propagation. */ static int flags_to_propagation_type(int flags) { |
7c6e984df
|
1613 |
int type = flags & ~(MS_REC | MS_SILENT); |
7a2e8a8fa
|
1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 |
/* Fail if any non-propagation flags are set */ if (type & ~(MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)) return 0; /* Only one propagation flag should be set */ if (!is_power_of_2(type)) return 0; return type; } /* |
07b20889e
|
1625 1626 |
* recursively change the type of the mountpoint. */ |
0a0d8a467
|
1627 |
static int do_change_type(struct path *path, int flag) |
07b20889e
|
1628 |
{ |
2d92ab3c6
|
1629 |
struct vfsmount *m, *mnt = path->mnt; |
07b20889e
|
1630 |
int recurse = flag & MS_REC; |
7a2e8a8fa
|
1631 |
int type; |
719f5d7f0
|
1632 |
int err = 0; |
07b20889e
|
1633 |
|
ee6f95829
|
1634 1635 |
if (!capable(CAP_SYS_ADMIN)) return -EPERM; |
2d92ab3c6
|
1636 |
if (path->dentry != path->mnt->mnt_root) |
07b20889e
|
1637 |
return -EINVAL; |
7a2e8a8fa
|
1638 1639 1640 |
type = flags_to_propagation_type(flag); if (!type) return -EINVAL; |
07b20889e
|
1641 |
down_write(&namespace_sem); |
719f5d7f0
|
1642 1643 1644 1645 1646 |
if (type == MS_SHARED) { err = invent_group_ids(mnt, recurse); if (err) goto out_unlock; } |
99b7db7b8
|
1647 |
br_write_lock(vfsmount_lock); |
07b20889e
|
1648 1649 |
for (m = mnt; m; m = (recurse ? next_mnt(m, mnt) : NULL)) change_mnt_propagation(m, type); |
99b7db7b8
|
1650 |
br_write_unlock(vfsmount_lock); |
719f5d7f0
|
1651 1652 |
out_unlock: |
07b20889e
|
1653 |
up_write(&namespace_sem); |
719f5d7f0
|
1654 |
return err; |
07b20889e
|
1655 1656 1657 |
} /* |
1da177e4c
|
1658 1659 |
* do loopback mount. */ |
0a0d8a467
|
1660 |
static int do_loopback(struct path *path, char *old_name, |
2dafe1c4d
|
1661 |
int recurse) |
1da177e4c
|
1662 |
{ |
b12cea919
|
1663 |
LIST_HEAD(umount_list); |
2d92ab3c6
|
1664 |
struct path old_path; |
1da177e4c
|
1665 |
struct vfsmount *mnt = NULL; |
2d92ab3c6
|
1666 |
int err = mount_is_safe(path); |
1da177e4c
|
1667 1668 1669 1670 |
if (err) return err; if (!old_name || !*old_name) return -EINVAL; |
815d405ce
|
1671 |
err = kern_path(old_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); |
1da177e4c
|
1672 1673 |
if (err) return err; |
b12cea919
|
1674 1675 1676 |
err = lock_mount(path); if (err) goto out; |
1da177e4c
|
1677 |
err = -EINVAL; |
2d92ab3c6
|
1678 |
if (IS_MNT_UNBINDABLE(old_path.mnt)) |
b12cea919
|
1679 |
goto out2; |
9676f0c63
|
1680 |
|
2d92ab3c6
|
1681 |
if (!check_mnt(path->mnt) || !check_mnt(old_path.mnt)) |
b12cea919
|
1682 |
goto out2; |
1da177e4c
|
1683 |
|
ccd48bc7f
|
1684 1685 |
err = -ENOMEM; if (recurse) |
2d92ab3c6
|
1686 |
mnt = copy_tree(old_path.mnt, old_path.dentry, 0); |
ccd48bc7f
|
1687 |
else |
2d92ab3c6
|
1688 |
mnt = clone_mnt(old_path.mnt, old_path.dentry, 0); |
ccd48bc7f
|
1689 1690 |
if (!mnt) |
b12cea919
|
1691 |
goto out2; |
ccd48bc7f
|
1692 |
|
2d92ab3c6
|
1693 |
err = graft_tree(mnt, path); |
ccd48bc7f
|
1694 |
if (err) { |
99b7db7b8
|
1695 |
br_write_lock(vfsmount_lock); |
a05964f39
|
1696 |
umount_tree(mnt, 0, &umount_list); |
99b7db7b8
|
1697 |
br_write_unlock(vfsmount_lock); |
5b83d2c5c
|
1698 |
} |
b12cea919
|
1699 1700 1701 |
out2: unlock_mount(path); release_mounts(&umount_list); |
ccd48bc7f
|
1702 |
out: |
2d92ab3c6
|
1703 |
path_put(&old_path); |
1da177e4c
|
1704 1705 |
return err; } |
2e4b7fcd9
|
1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 |
static int change_mount_flags(struct vfsmount *mnt, int ms_flags) { int error = 0; int readonly_request = 0; if (ms_flags & MS_RDONLY) readonly_request = 1; if (readonly_request == __mnt_is_readonly(mnt)) return 0; if (readonly_request) error = mnt_make_readonly(mnt); else __mnt_unmake_readonly(mnt); return error; } |
1da177e4c
|
1722 1723 1724 1725 1726 |
/* * change filesystem flags. dir should be a physical root of filesystem. * If you've mounted a non-root directory somewhere and want to do remount * on it - tough luck. */ |
0a0d8a467
|
1727 |
static int do_remount(struct path *path, int flags, int mnt_flags, |
1da177e4c
|
1728 1729 1730 |
void *data) { int err; |
2d92ab3c6
|
1731 |
struct super_block *sb = path->mnt->mnt_sb; |
1da177e4c
|
1732 1733 1734 |
if (!capable(CAP_SYS_ADMIN)) return -EPERM; |
2d92ab3c6
|
1735 |
if (!check_mnt(path->mnt)) |
1da177e4c
|
1736 |
return -EINVAL; |
2d92ab3c6
|
1737 |
if (path->dentry != path->mnt->mnt_root) |
1da177e4c
|
1738 |
return -EINVAL; |
ff36fe2c8
|
1739 1740 1741 |
err = security_sb_remount(sb, data); if (err) return err; |
1da177e4c
|
1742 |
down_write(&sb->s_umount); |
2e4b7fcd9
|
1743 |
if (flags & MS_BIND) |
2d92ab3c6
|
1744 |
err = change_mount_flags(path->mnt, flags); |
4aa98cf76
|
1745 |
else |
2e4b7fcd9
|
1746 |
err = do_remount_sb(sb, flags, data, 0); |
7b43a79f3
|
1747 |
if (!err) { |
99b7db7b8
|
1748 |
br_write_lock(vfsmount_lock); |
495d6c9c6
|
1749 |
mnt_flags |= path->mnt->mnt_flags & MNT_PROPAGATION_MASK; |
2d92ab3c6
|
1750 |
path->mnt->mnt_flags = mnt_flags; |
99b7db7b8
|
1751 |
br_write_unlock(vfsmount_lock); |
7b43a79f3
|
1752 |
} |
1da177e4c
|
1753 |
up_write(&sb->s_umount); |
0e55a7cca
|
1754 |
if (!err) { |
99b7db7b8
|
1755 |
br_write_lock(vfsmount_lock); |
0e55a7cca
|
1756 |
touch_mnt_namespace(path->mnt->mnt_ns); |
99b7db7b8
|
1757 |
br_write_unlock(vfsmount_lock); |
0e55a7cca
|
1758 |
} |
1da177e4c
|
1759 1760 |
return err; } |
9676f0c63
|
1761 1762 1763 1764 1765 1766 1767 1768 1769 |
static inline int tree_contains_unbindable(struct vfsmount *mnt) { struct vfsmount *p; for (p = mnt; p; p = next_mnt(p, mnt)) { if (IS_MNT_UNBINDABLE(p)) return 1; } return 0; } |
0a0d8a467
|
1770 |
static int do_move_mount(struct path *path, char *old_name) |
1da177e4c
|
1771 |
{ |
2d92ab3c6
|
1772 |
struct path old_path, parent_path; |
1da177e4c
|
1773 1774 1775 1776 1777 1778 |
struct vfsmount *p; int err = 0; if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (!old_name || !*old_name) return -EINVAL; |
2d92ab3c6
|
1779 |
err = kern_path(old_name, LOOKUP_FOLLOW, &old_path); |
1da177e4c
|
1780 1781 |
if (err) return err; |
b12cea919
|
1782 |
err = lock_mount(path); |
cc53ce53c
|
1783 1784 |
if (err < 0) goto out; |
1da177e4c
|
1785 |
err = -EINVAL; |
2d92ab3c6
|
1786 |
if (!check_mnt(path->mnt) || !check_mnt(old_path.mnt)) |
1da177e4c
|
1787 |
goto out1; |
f3da392e9
|
1788 |
if (d_unlinked(path->dentry)) |
214444032
|
1789 |
goto out1; |
1da177e4c
|
1790 1791 |
err = -EINVAL; |
2d92ab3c6
|
1792 |
if (old_path.dentry != old_path.mnt->mnt_root) |
214444032
|
1793 |
goto out1; |
1da177e4c
|
1794 |
|
b2dba1af3
|
1795 |
if (!mnt_has_parent(old_path.mnt)) |
214444032
|
1796 |
goto out1; |
1da177e4c
|
1797 |
|
2d92ab3c6
|
1798 1799 |
if (S_ISDIR(path->dentry->d_inode->i_mode) != S_ISDIR(old_path.dentry->d_inode->i_mode)) |
214444032
|
1800 1801 1802 1803 |
goto out1; /* * Don't move a mount residing in a shared parent. */ |
afac7cba7
|
1804 |
if (IS_MNT_SHARED(old_path.mnt->mnt_parent)) |
214444032
|
1805 |
goto out1; |
9676f0c63
|
1806 1807 1808 1809 |
/* * Don't move a mount tree containing unbindable mounts to a destination * mount which is shared. */ |
2d92ab3c6
|
1810 1811 |
if (IS_MNT_SHARED(path->mnt) && tree_contains_unbindable(old_path.mnt)) |
9676f0c63
|
1812 |
goto out1; |
1da177e4c
|
1813 |
err = -ELOOP; |
b2dba1af3
|
1814 |
for (p = path->mnt; mnt_has_parent(p); p = p->mnt_parent) |
2d92ab3c6
|
1815 |
if (p == old_path.mnt) |
214444032
|
1816 |
goto out1; |
1da177e4c
|
1817 |
|
2d92ab3c6
|
1818 |
err = attach_recursive_mnt(old_path.mnt, path, &parent_path); |
4ac913785
|
1819 |
if (err) |
214444032
|
1820 |
goto out1; |
1da177e4c
|
1821 1822 1823 |
/* if the mount is moved, it should no longer be expire * automatically */ |
2d92ab3c6
|
1824 |
list_del_init(&old_path.mnt->mnt_expire); |
1da177e4c
|
1825 |
out1: |
b12cea919
|
1826 |
unlock_mount(path); |
1da177e4c
|
1827 |
out: |
1da177e4c
|
1828 |
if (!err) |
1a3906895
|
1829 |
path_put(&parent_path); |
2d92ab3c6
|
1830 |
path_put(&old_path); |
1da177e4c
|
1831 1832 |
return err; } |
9d412a43c
|
1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 |
static struct vfsmount *fs_set_subtype(struct vfsmount *mnt, const char *fstype) { int err; const char *subtype = strchr(fstype, '.'); if (subtype) { subtype++; err = -EINVAL; if (!subtype[0]) goto err; } else subtype = ""; mnt->mnt_sb->s_subtype = kstrdup(subtype, GFP_KERNEL); err = -ENOMEM; if (!mnt->mnt_sb->s_subtype) goto err; return mnt; err: mntput(mnt); return ERR_PTR(err); } |
79e801a90
|
1855 |
static struct vfsmount * |
9d412a43c
|
1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 |
do_kern_mount(const char *fstype, int flags, const char *name, void *data) { struct file_system_type *type = get_fs_type(fstype); struct vfsmount *mnt; if (!type) return ERR_PTR(-ENODEV); mnt = vfs_kern_mount(type, flags, name, data); if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) && !mnt->mnt_sb->s_subtype) mnt = fs_set_subtype(mnt, fstype); put_filesystem(type); return mnt; } |
9d412a43c
|
1869 1870 1871 1872 1873 1874 1875 1876 1877 |
/* * add a mount into a namespace's mount tree */ static int do_add_mount(struct vfsmount *newmnt, struct path *path, int mnt_flags) { int err; mnt_flags &= ~(MNT_SHARED | MNT_WRITE_HOLD | MNT_INTERNAL); |
b12cea919
|
1878 1879 1880 |
err = lock_mount(path); if (err) return err; |
9d412a43c
|
1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 |
err = -EINVAL; if (!(mnt_flags & MNT_SHRINKABLE) && !check_mnt(path->mnt)) goto unlock; /* Refuse the same filesystem on the same mount point */ err = -EBUSY; if (path->mnt->mnt_sb == newmnt->mnt_sb && path->mnt->mnt_root == path->dentry) goto unlock; err = -EINVAL; if (S_ISLNK(newmnt->mnt_root->d_inode->i_mode)) goto unlock; newmnt->mnt_flags = mnt_flags; err = graft_tree(newmnt, path); unlock: |
b12cea919
|
1900 |
unlock_mount(path); |
9d412a43c
|
1901 1902 |
return err; } |
b1e75df45
|
1903 |
|
1da177e4c
|
1904 1905 1906 1907 |
/* * create a new mount for userspace and request it to be added into the * namespace's tree */ |
0a0d8a467
|
1908 |
static int do_new_mount(struct path *path, char *type, int flags, |
1da177e4c
|
1909 1910 1911 |
int mnt_flags, char *name, void *data) { struct vfsmount *mnt; |
15f9a3f3e
|
1912 |
int err; |
1da177e4c
|
1913 |
|
eca6f534e
|
1914 |
if (!type) |
1da177e4c
|
1915 1916 1917 1918 1919 1920 1921 1922 1923 |
return -EINVAL; /* we need capabilities... */ if (!capable(CAP_SYS_ADMIN)) return -EPERM; mnt = do_kern_mount(type, flags, name, data); if (IS_ERR(mnt)) return PTR_ERR(mnt); |
15f9a3f3e
|
1924 1925 1926 1927 |
err = do_add_mount(mnt, path, mnt_flags); if (err) mntput(mnt); return err; |
1da177e4c
|
1928 |
} |
19a167af7
|
1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 |
int finish_automount(struct vfsmount *m, struct path *path) { int err; /* The new mount record should have at least 2 refs to prevent it being * expired before we get a chance to add it */ BUG_ON(mnt_get_count(m) < 2); if (m->mnt_sb == path->mnt->mnt_sb && m->mnt_root == path->dentry) { |
b1e75df45
|
1939 1940 |
err = -ELOOP; goto fail; |
19a167af7
|
1941 |
} |
19a167af7
|
1942 |
err = do_add_mount(m, path, path->mnt->mnt_flags | MNT_SHRINKABLE); |
b1e75df45
|
1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 |
if (!err) return 0; fail: /* remove m from any expiration list it may be on */ if (!list_empty(&m->mnt_expire)) { down_write(&namespace_sem); br_write_lock(vfsmount_lock); list_del_init(&m->mnt_expire); br_write_unlock(vfsmount_lock); up_write(&namespace_sem); |
19a167af7
|
1953 |
} |
b1e75df45
|
1954 1955 |
mntput(m); mntput(m); |
19a167af7
|
1956 1957 |
return err; } |
ea5b778a8
|
1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 |
/** * mnt_set_expiry - Put a mount on an expiration list * @mnt: The mount to list. * @expiry_list: The list to add the mount to. */ void mnt_set_expiry(struct vfsmount *mnt, struct list_head *expiry_list) { down_write(&namespace_sem); br_write_lock(vfsmount_lock); list_add_tail(&mnt->mnt_expire, expiry_list); br_write_unlock(vfsmount_lock); up_write(&namespace_sem); } EXPORT_SYMBOL(mnt_set_expiry); /* |
1da177e4c
|
1976 1977 1978 1979 1980 1981 |
* process a list of expirable mountpoints with the intent of discarding any * mountpoints that aren't in use and haven't been touched since last we came * here */ void mark_mounts_for_expiry(struct list_head *mounts) { |
1da177e4c
|
1982 1983 |
struct vfsmount *mnt, *next; LIST_HEAD(graveyard); |
bcc5c7d2b
|
1984 |
LIST_HEAD(umounts); |
1da177e4c
|
1985 1986 1987 |
if (list_empty(mounts)) return; |
bcc5c7d2b
|
1988 |
down_write(&namespace_sem); |
99b7db7b8
|
1989 |
br_write_lock(vfsmount_lock); |
1da177e4c
|
1990 1991 1992 1993 1994 1995 1996 |
/* extract from the expiration list every vfsmount that matches the * following criteria: * - only referenced by its parent vfsmount * - still marked for expiry (marked on the last call here; marks are * cleared by mntput()) */ |
55e700b92
|
1997 |
list_for_each_entry_safe(mnt, next, mounts, mnt_expire) { |
1da177e4c
|
1998 |
if (!xchg(&mnt->mnt_expiry_mark, 1) || |
bcc5c7d2b
|
1999 |
propagate_mount_busy(mnt, 1)) |
1da177e4c
|
2000 |
continue; |
55e700b92
|
2001 |
list_move(&mnt->mnt_expire, &graveyard); |
1da177e4c
|
2002 |
} |
bcc5c7d2b
|
2003 2004 2005 2006 2007 |
while (!list_empty(&graveyard)) { mnt = list_first_entry(&graveyard, struct vfsmount, mnt_expire); touch_mnt_namespace(mnt->mnt_ns); umount_tree(mnt, 1, &umounts); } |
99b7db7b8
|
2008 |
br_write_unlock(vfsmount_lock); |
bcc5c7d2b
|
2009 2010 2011 |
up_write(&namespace_sem); release_mounts(&umounts); |
5528f911b
|
2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 |
} EXPORT_SYMBOL_GPL(mark_mounts_for_expiry); /* * Ripoff of 'select_parent()' * * search the list of submounts for a given mountpoint, and move any * shrinkable submounts to the 'graveyard' list. */ static int select_submounts(struct vfsmount *parent, struct list_head *graveyard) { struct vfsmount *this_parent = parent; struct list_head *next; int found = 0; repeat: next = this_parent->mnt_mounts.next; resume: while (next != &this_parent->mnt_mounts) { struct list_head *tmp = next; struct vfsmount *mnt = list_entry(tmp, struct vfsmount, mnt_child); next = tmp->next; if (!(mnt->mnt_flags & MNT_SHRINKABLE)) |
1da177e4c
|
2037 |
continue; |
5528f911b
|
2038 2039 2040 2041 2042 2043 2044 |
/* * Descend a level if the d_mounts list is non-empty. */ if (!list_empty(&mnt->mnt_mounts)) { this_parent = mnt; goto repeat; } |
1da177e4c
|
2045 |
|
5528f911b
|
2046 |
if (!propagate_mount_busy(mnt, 1)) { |
5528f911b
|
2047 2048 2049 |
list_move_tail(&mnt->mnt_expire, graveyard); found++; } |
1da177e4c
|
2050 |
} |
5528f911b
|
2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 |
/* * All done at this level ... ascend and resume the search */ if (this_parent != parent) { next = this_parent->mnt_child.next; this_parent = this_parent->mnt_parent; goto resume; } return found; } /* * process a list of expirable mountpoints with the intent of discarding any * submounts of a specific parent mountpoint |
99b7db7b8
|
2065 2066 |
* * vfsmount_lock must be held for write |
5528f911b
|
2067 |
*/ |
c35038bec
|
2068 |
static void shrink_submounts(struct vfsmount *mnt, struct list_head *umounts) |
5528f911b
|
2069 2070 |
{ LIST_HEAD(graveyard); |
c35038bec
|
2071 |
struct vfsmount *m; |
5528f911b
|
2072 |
|
5528f911b
|
2073 |
/* extract submounts of 'mountpoint' from the expiration list */ |
c35038bec
|
2074 |
while (select_submounts(mnt, &graveyard)) { |
bcc5c7d2b
|
2075 |
while (!list_empty(&graveyard)) { |
c35038bec
|
2076 |
m = list_first_entry(&graveyard, struct vfsmount, |
bcc5c7d2b
|
2077 |
mnt_expire); |
afef80b3d
|
2078 2079 |
touch_mnt_namespace(m->mnt_ns); umount_tree(m, 1, umounts); |
bcc5c7d2b
|
2080 2081 |
} } |
1da177e4c
|
2082 |
} |
1da177e4c
|
2083 2084 2085 2086 2087 2088 |
/* * Some copy_from_user() implementations do not return the exact number of * bytes remaining to copy on a fault. But copy_mount_options() requires that. * Note that this function differs from copy_from_user() in that it will oops * on bad values of `to', rather than returning a short copy. */ |
b58fed8b1
|
2089 2090 |
static long exact_copy_from_user(void *to, const void __user * from, unsigned long n) |
1da177e4c
|
2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 |
{ char *t = to; const char __user *f = from; char c; if (!access_ok(VERIFY_READ, from, n)) return n; while (n) { if (__get_user(c, f)) { memset(t, 0, n); break; } *t++ = c; f++; n--; } return n; } |
b58fed8b1
|
2110 |
int copy_mount_options(const void __user * data, unsigned long *where) |
1da177e4c
|
2111 2112 2113 2114 |
{ int i; unsigned long page; unsigned long size; |
b58fed8b1
|
2115 |
|
1da177e4c
|
2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 |
*where = 0; if (!data) return 0; if (!(page = __get_free_page(GFP_KERNEL))) return -ENOMEM; /* We only care that *some* data at the address the user * gave us is valid. Just in case, we'll zero * the remainder of the page. */ /* copy_from_user cannot cross TASK_SIZE ! */ size = TASK_SIZE - (unsigned long)data; if (size > PAGE_SIZE) size = PAGE_SIZE; i = size - exact_copy_from_user((void *)page, data, size); if (!i) { |
b58fed8b1
|
2134 |
free_page(page); |
1da177e4c
|
2135 2136 2137 2138 2139 2140 2141 |
return -EFAULT; } if (i != PAGE_SIZE) memset((char *)page + i, 0, PAGE_SIZE - i); *where = page; return 0; } |
eca6f534e
|
2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 |
int copy_mount_string(const void __user *data, char **where) { char *tmp; if (!data) { *where = NULL; return 0; } tmp = strndup_user(data, PAGE_SIZE); if (IS_ERR(tmp)) return PTR_ERR(tmp); *where = tmp; return 0; } |
1da177e4c
|
2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 |
/* * Flags is a 32-bit value that allows up to 31 non-fs dependent flags to * be given to the mount() call (ie: read-only, no-dev, no-suid etc). * * data is a (void *) that can point to any structure up to * PAGE_SIZE-1 bytes, which can contain arbitrary fs-dependent * information (or be NULL). * * Pre-0.97 versions of mount() didn't have a flags word. * When the flags word was introduced its top half was required * to have the magic value 0xC0ED, and this remained so until 2.4.0-test9. * Therefore, if this magic number is present, it carries no information * and must be discarded. */ |
b58fed8b1
|
2172 |
long do_mount(char *dev_name, char *dir_name, char *type_page, |
1da177e4c
|
2173 2174 |
unsigned long flags, void *data_page) { |
2d92ab3c6
|
2175 |
struct path path; |
1da177e4c
|
2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 |
int retval = 0; int mnt_flags = 0; /* Discard magic */ if ((flags & MS_MGC_MSK) == MS_MGC_VAL) flags &= ~MS_MGC_MSK; /* Basic sanity checks */ if (!dir_name || !*dir_name || !memchr(dir_name, 0, PAGE_SIZE)) return -EINVAL; |
1da177e4c
|
2187 2188 2189 |
if (data_page) ((char *)data_page)[PAGE_SIZE - 1] = 0; |
a27ab9f26
|
2190 2191 2192 2193 2194 2195 2196 2197 2198 |
/* ... and get the mountpoint */ retval = kern_path(dir_name, LOOKUP_FOLLOW, &path); if (retval) return retval; retval = security_sb_mount(dev_name, &path, type_page, flags, data_page); if (retval) goto dput_out; |
613cbe3d4
|
2199 2200 2201 |
/* Default to relatime unless overriden */ if (!(flags & MS_NOATIME)) mnt_flags |= MNT_RELATIME; |
0a1c01c94
|
2202 |
|
1da177e4c
|
2203 2204 2205 2206 2207 2208 2209 |
/* Separate the per-mountpoint flags */ if (flags & MS_NOSUID) mnt_flags |= MNT_NOSUID; if (flags & MS_NODEV) mnt_flags |= MNT_NODEV; if (flags & MS_NOEXEC) mnt_flags |= MNT_NOEXEC; |
fc33a7bb9
|
2210 2211 2212 2213 |
if (flags & MS_NOATIME) mnt_flags |= MNT_NOATIME; if (flags & MS_NODIRATIME) mnt_flags |= MNT_NODIRATIME; |
d0adde574
|
2214 2215 |
if (flags & MS_STRICTATIME) mnt_flags &= ~(MNT_RELATIME | MNT_NOATIME); |
2e4b7fcd9
|
2216 2217 |
if (flags & MS_RDONLY) mnt_flags |= MNT_READONLY; |
fc33a7bb9
|
2218 |
|
7a4dec538
|
2219 |
flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE | MS_BORN | |
d0adde574
|
2220 2221 |
MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT | MS_STRICTATIME); |
1da177e4c
|
2222 |
|
1da177e4c
|
2223 |
if (flags & MS_REMOUNT) |
2d92ab3c6
|
2224 |
retval = do_remount(&path, flags & ~MS_REMOUNT, mnt_flags, |
1da177e4c
|
2225 2226 |
data_page); else if (flags & MS_BIND) |
2d92ab3c6
|
2227 |
retval = do_loopback(&path, dev_name, flags & MS_REC); |
9676f0c63
|
2228 |
else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)) |
2d92ab3c6
|
2229 |
retval = do_change_type(&path, flags); |
1da177e4c
|
2230 |
else if (flags & MS_MOVE) |
2d92ab3c6
|
2231 |
retval = do_move_mount(&path, dev_name); |
1da177e4c
|
2232 |
else |
2d92ab3c6
|
2233 |
retval = do_new_mount(&path, type_page, flags, mnt_flags, |
1da177e4c
|
2234 2235 |
dev_name, data_page); dput_out: |
2d92ab3c6
|
2236 |
path_put(&path); |
1da177e4c
|
2237 2238 |
return retval; } |
cf8d2c11c
|
2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 |
static struct mnt_namespace *alloc_mnt_ns(void) { struct mnt_namespace *new_ns; new_ns = kmalloc(sizeof(struct mnt_namespace), GFP_KERNEL); if (!new_ns) return ERR_PTR(-ENOMEM); atomic_set(&new_ns->count, 1); new_ns->root = NULL; INIT_LIST_HEAD(&new_ns->list); init_waitqueue_head(&new_ns->poll); new_ns->event = 0; return new_ns; } |
f03c65993
|
2253 2254 |
void mnt_make_longterm(struct vfsmount *mnt) { |
7e3d0eb0b
|
2255 |
__mnt_make_longterm(mnt); |
f03c65993
|
2256 2257 2258 2259 |
} void mnt_make_shortterm(struct vfsmount *mnt) { |
7e3d0eb0b
|
2260 |
#ifdef CONFIG_SMP |
f03c65993
|
2261 2262 2263 2264 2265 |
if (atomic_add_unless(&mnt->mnt_longterm, -1, 1)) return; br_write_lock(vfsmount_lock); atomic_dec(&mnt->mnt_longterm); br_write_unlock(vfsmount_lock); |
7e3d0eb0b
|
2266 |
#endif |
f03c65993
|
2267 |
} |
741a29513
|
2268 2269 2270 2271 |
/* * Allocate a new namespace structure and populate it with contents * copied from the namespace of the passed in task structure. */ |
e3222c4ec
|
2272 |
static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns, |
6b3286ed1
|
2273 |
struct fs_struct *fs) |
1da177e4c
|
2274 |
{ |
6b3286ed1
|
2275 |
struct mnt_namespace *new_ns; |
7f2da1e7d
|
2276 |
struct vfsmount *rootmnt = NULL, *pwdmnt = NULL; |
1da177e4c
|
2277 |
struct vfsmount *p, *q; |
cf8d2c11c
|
2278 2279 2280 |
new_ns = alloc_mnt_ns(); if (IS_ERR(new_ns)) return new_ns; |
1da177e4c
|
2281 |
|
390c68436
|
2282 |
down_write(&namespace_sem); |
1da177e4c
|
2283 |
/* First pass: copy the tree topology */ |
6b3286ed1
|
2284 |
new_ns->root = copy_tree(mnt_ns->root, mnt_ns->root->mnt_root, |
9676f0c63
|
2285 |
CL_COPY_ALL | CL_EXPIRE); |
1da177e4c
|
2286 |
if (!new_ns->root) { |
390c68436
|
2287 |
up_write(&namespace_sem); |
1da177e4c
|
2288 |
kfree(new_ns); |
5cc4a0341
|
2289 |
return ERR_PTR(-ENOMEM); |
1da177e4c
|
2290 |
} |
99b7db7b8
|
2291 |
br_write_lock(vfsmount_lock); |
1da177e4c
|
2292 |
list_add_tail(&new_ns->list, &new_ns->root->mnt_list); |
99b7db7b8
|
2293 |
br_write_unlock(vfsmount_lock); |
1da177e4c
|
2294 2295 2296 2297 2298 2299 |
/* * Second pass: switch the tsk->fs->* elements and mark new vfsmounts * as belonging to new namespace. We have already acquired a private * fs_struct, so tsk->fs->lock is not needed. */ |
6b3286ed1
|
2300 |
p = mnt_ns->root; |
1da177e4c
|
2301 2302 |
q = new_ns->root; while (p) { |
6b3286ed1
|
2303 |
q->mnt_ns = new_ns; |
7e3d0eb0b
|
2304 |
__mnt_make_longterm(q); |
1da177e4c
|
2305 |
if (fs) { |
6ac08c39a
|
2306 |
if (p == fs->root.mnt) { |
f03c65993
|
2307 |
fs->root.mnt = mntget(q); |
7e3d0eb0b
|
2308 |
__mnt_make_longterm(q); |
f03c65993
|
2309 |
mnt_make_shortterm(p); |
1da177e4c
|
2310 |
rootmnt = p; |
1da177e4c
|
2311 |
} |
6ac08c39a
|
2312 |
if (p == fs->pwd.mnt) { |
f03c65993
|
2313 |
fs->pwd.mnt = mntget(q); |
7e3d0eb0b
|
2314 |
__mnt_make_longterm(q); |
f03c65993
|
2315 |
mnt_make_shortterm(p); |
1da177e4c
|
2316 |
pwdmnt = p; |
1da177e4c
|
2317 |
} |
1da177e4c
|
2318 |
} |
6b3286ed1
|
2319 |
p = next_mnt(p, mnt_ns->root); |
1da177e4c
|
2320 2321 |
q = next_mnt(q, new_ns->root); } |
390c68436
|
2322 |
up_write(&namespace_sem); |
1da177e4c
|
2323 |
|
1da177e4c
|
2324 |
if (rootmnt) |
f03c65993
|
2325 |
mntput(rootmnt); |
1da177e4c
|
2326 |
if (pwdmnt) |
f03c65993
|
2327 |
mntput(pwdmnt); |
1da177e4c
|
2328 |
|
741a29513
|
2329 2330 |
return new_ns; } |
213dd266d
|
2331 |
struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns, |
e3222c4ec
|
2332 |
struct fs_struct *new_fs) |
741a29513
|
2333 |
{ |
6b3286ed1
|
2334 |
struct mnt_namespace *new_ns; |
741a29513
|
2335 |
|
e3222c4ec
|
2336 |
BUG_ON(!ns); |
6b3286ed1
|
2337 |
get_mnt_ns(ns); |
741a29513
|
2338 2339 |
if (!(flags & CLONE_NEWNS)) |
e3222c4ec
|
2340 |
return ns; |
741a29513
|
2341 |
|
e3222c4ec
|
2342 |
new_ns = dup_mnt_ns(ns, new_fs); |
741a29513
|
2343 |
|
6b3286ed1
|
2344 |
put_mnt_ns(ns); |
e3222c4ec
|
2345 |
return new_ns; |
1da177e4c
|
2346 |
} |
cf8d2c11c
|
2347 2348 2349 2350 |
/** * create_mnt_ns - creates a private namespace and adds a root filesystem * @mnt: pointer to the new root filesystem mountpoint */ |
6c449c8df
|
2351 |
static struct mnt_namespace *create_mnt_ns(struct vfsmount *mnt) |
cf8d2c11c
|
2352 2353 2354 2355 2356 2357 |
{ struct mnt_namespace *new_ns; new_ns = alloc_mnt_ns(); if (!IS_ERR(new_ns)) { mnt->mnt_ns = new_ns; |
7e3d0eb0b
|
2358 |
__mnt_make_longterm(mnt); |
cf8d2c11c
|
2359 2360 |
new_ns->root = mnt; list_add(&new_ns->list, &new_ns->root->mnt_list); |
c13344958
|
2361 2362 |
} else { mntput(mnt); |
cf8d2c11c
|
2363 2364 2365 |
} return new_ns; } |
cf8d2c11c
|
2366 |
|
ea441d110
|
2367 2368 2369 |
struct dentry *mount_subtree(struct vfsmount *mnt, const char *name) { struct mnt_namespace *ns; |
d31da0f0b
|
2370 |
struct super_block *s; |
ea441d110
|
2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 |
struct path path; int err; ns = create_mnt_ns(mnt); if (IS_ERR(ns)) return ERR_CAST(ns); err = vfs_path_lookup(mnt->mnt_root, mnt, name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &path); put_mnt_ns(ns); if (err) return ERR_PTR(err); /* trade a vfsmount reference for active sb one */ |
d31da0f0b
|
2387 2388 |
s = path.mnt->mnt_sb; atomic_inc(&s->s_active); |
ea441d110
|
2389 2390 |
mntput(path.mnt); /* lock the sucker */ |
d31da0f0b
|
2391 |
down_write(&s->s_umount); |
ea441d110
|
2392 2393 2394 2395 |
/* ... and return the root of (sub)tree on it */ return path.dentry; } EXPORT_SYMBOL(mount_subtree); |
bdc480e3b
|
2396 2397 |
SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name, char __user *, type, unsigned long, flags, void __user *, data) |
1da177e4c
|
2398 |
{ |
eca6f534e
|
2399 2400 2401 2402 |
int ret; char *kernel_type; char *kernel_dir; char *kernel_dev; |
1da177e4c
|
2403 |
unsigned long data_page; |
1da177e4c
|
2404 |
|
eca6f534e
|
2405 2406 2407 |
ret = copy_mount_string(type, &kernel_type); if (ret < 0) goto out_type; |
1da177e4c
|
2408 |
|
eca6f534e
|
2409 2410 2411 2412 2413 |
kernel_dir = getname(dir_name); if (IS_ERR(kernel_dir)) { ret = PTR_ERR(kernel_dir); goto out_dir; } |
1da177e4c
|
2414 |
|
eca6f534e
|
2415 2416 2417 |
ret = copy_mount_string(dev_name, &kernel_dev); if (ret < 0) goto out_dev; |
1da177e4c
|
2418 |
|
eca6f534e
|
2419 2420 2421 |
ret = copy_mount_options(data, &data_page); if (ret < 0) goto out_data; |
1da177e4c
|
2422 |
|
eca6f534e
|
2423 2424 |
ret = do_mount(kernel_dev, kernel_dir, kernel_type, flags, (void *) data_page); |
1da177e4c
|
2425 |
|
eca6f534e
|
2426 2427 2428 2429 2430 2431 2432 2433 2434 |
free_page(data_page); out_data: kfree(kernel_dev); out_dev: putname(kernel_dir); out_dir: kfree(kernel_type); out_type: return ret; |
1da177e4c
|
2435 2436 2437 |
} /* |
afac7cba7
|
2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 |
* Return true if path is reachable from root * * namespace_sem or vfsmount_lock is held */ bool is_path_reachable(struct vfsmount *mnt, struct dentry *dentry, const struct path *root) { while (mnt != root->mnt && mnt_has_parent(mnt)) { dentry = mnt->mnt_mountpoint; mnt = mnt->mnt_parent; } return mnt == root->mnt && is_subdir(dentry, root->dentry); } int path_is_under(struct path *path1, struct path *path2) { int res; br_read_lock(vfsmount_lock); res = is_path_reachable(path1->mnt, path1->dentry, path2); br_read_unlock(vfsmount_lock); return res; } EXPORT_SYMBOL(path_is_under); /* |
1da177e4c
|
2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 |
* pivot_root Semantics: * Moves the root file system of the current process to the directory put_old, * makes new_root as the new root file system of the current process, and sets * root/cwd of all processes which had them on the current root to new_root. * * Restrictions: * The new_root and put_old must be directories, and must not be on the * same file system as the current process root. The put_old must be * underneath new_root, i.e. adding a non-zero number of /.. to the string * pointed to by put_old must yield the same directory as new_root. No other * file system may be mounted on put_old. After all, new_root is a mountpoint. * |
4a0d11fae
|
2475 2476 2477 2478 |
* Also, the current root cannot be on the 'rootfs' (initial ramfs) filesystem. * See Documentation/filesystems/ramfs-rootfs-initramfs.txt for alternatives * in this situation. * |
1da177e4c
|
2479 2480 2481 2482 2483 2484 2485 2486 |
* Notes: * - we don't move root/cwd if they are not at the root (reason: if something * cared enough to change them, it's probably wrong to force them elsewhere) * - it's okay to pick a root that isn't the root of a file system, e.g. * /nfs/my_root where /nfs is the mount point. It must be a mountpoint, * though, so you may need to say mount --bind /nfs/my_root /nfs/my_root * first. */ |
3480b2574
|
2487 2488 |
SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, const char __user *, put_old) |
1da177e4c
|
2489 |
{ |
2d8f30380
|
2490 |
struct path new, old, parent_path, root_parent, root; |
1da177e4c
|
2491 2492 2493 2494 |
int error; if (!capable(CAP_SYS_ADMIN)) return -EPERM; |
2d8f30380
|
2495 |
error = user_path_dir(new_root, &new); |
1da177e4c
|
2496 2497 |
if (error) goto out0; |
1da177e4c
|
2498 |
|
2d8f30380
|
2499 |
error = user_path_dir(put_old, &old); |
1da177e4c
|
2500 2501 |
if (error) goto out1; |
2d8f30380
|
2502 |
error = security_sb_pivotroot(&old, &new); |
b12cea919
|
2503 2504 |
if (error) goto out2; |
1da177e4c
|
2505 |
|
f7ad3c6be
|
2506 |
get_fs_root(current->fs, &root); |
b12cea919
|
2507 2508 2509 |
error = lock_mount(&old); if (error) goto out3; |
1da177e4c
|
2510 |
error = -EINVAL; |
2d8f30380
|
2511 2512 |
if (IS_MNT_SHARED(old.mnt) || IS_MNT_SHARED(new.mnt->mnt_parent) || |
8c3ee42e8
|
2513 |
IS_MNT_SHARED(root.mnt->mnt_parent)) |
b12cea919
|
2514 |
goto out4; |
27cb1572e
|
2515 |
if (!check_mnt(root.mnt) || !check_mnt(new.mnt)) |
b12cea919
|
2516 |
goto out4; |
1da177e4c
|
2517 |
error = -ENOENT; |
f3da392e9
|
2518 |
if (d_unlinked(new.dentry)) |
b12cea919
|
2519 |
goto out4; |
f3da392e9
|
2520 |
if (d_unlinked(old.dentry)) |
b12cea919
|
2521 |
goto out4; |
1da177e4c
|
2522 |
error = -EBUSY; |
2d8f30380
|
2523 2524 |
if (new.mnt == root.mnt || old.mnt == root.mnt) |
b12cea919
|
2525 |
goto out4; /* loop, on the same file system */ |
1da177e4c
|
2526 |
error = -EINVAL; |
8c3ee42e8
|
2527 |
if (root.mnt->mnt_root != root.dentry) |
b12cea919
|
2528 |
goto out4; /* not a mountpoint */ |
b2dba1af3
|
2529 |
if (!mnt_has_parent(root.mnt)) |
b12cea919
|
2530 |
goto out4; /* not attached */ |
2d8f30380
|
2531 |
if (new.mnt->mnt_root != new.dentry) |
b12cea919
|
2532 |
goto out4; /* not a mountpoint */ |
b2dba1af3
|
2533 |
if (!mnt_has_parent(new.mnt)) |
b12cea919
|
2534 |
goto out4; /* not attached */ |
4ac913785
|
2535 |
/* make sure we can reach put_old from new_root */ |
afac7cba7
|
2536 |
if (!is_path_reachable(old.mnt, old.dentry, &new)) |
b12cea919
|
2537 |
goto out4; |
27cb1572e
|
2538 |
br_write_lock(vfsmount_lock); |
2d8f30380
|
2539 |
detach_mnt(new.mnt, &parent_path); |
8c3ee42e8
|
2540 |
detach_mnt(root.mnt, &root_parent); |
4ac913785
|
2541 |
/* mount old root on put_old */ |
2d8f30380
|
2542 |
attach_mnt(root.mnt, &old); |
4ac913785
|
2543 |
/* mount new_root on / */ |
2d8f30380
|
2544 |
attach_mnt(new.mnt, &root_parent); |
6b3286ed1
|
2545 |
touch_mnt_namespace(current->nsproxy->mnt_ns); |
99b7db7b8
|
2546 |
br_write_unlock(vfsmount_lock); |
2d8f30380
|
2547 |
chroot_fs_refs(&root, &new); |
1da177e4c
|
2548 |
error = 0; |
b12cea919
|
2549 2550 2551 2552 2553 2554 2555 |
out4: unlock_mount(&old); if (!error) { path_put(&root_parent); path_put(&parent_path); } out3: |
8c3ee42e8
|
2556 |
path_put(&root); |
b12cea919
|
2557 |
out2: |
2d8f30380
|
2558 |
path_put(&old); |
1da177e4c
|
2559 |
out1: |
2d8f30380
|
2560 |
path_put(&new); |
1da177e4c
|
2561 |
out0: |
1da177e4c
|
2562 |
return error; |
1da177e4c
|
2563 2564 2565 2566 2567 |
} static void __init init_mount_tree(void) { struct vfsmount *mnt; |
6b3286ed1
|
2568 |
struct mnt_namespace *ns; |
ac748a09f
|
2569 |
struct path root; |
1da177e4c
|
2570 2571 2572 2573 |
mnt = do_kern_mount("rootfs", 0, "rootfs", NULL); if (IS_ERR(mnt)) panic("Can't create rootfs"); |
b3e19d924
|
2574 |
|
3b22edc57
|
2575 2576 |
ns = create_mnt_ns(mnt); if (IS_ERR(ns)) |
1da177e4c
|
2577 |
panic("Can't allocate initial namespace"); |
6b3286ed1
|
2578 2579 2580 |
init_task.nsproxy->mnt_ns = ns; get_mnt_ns(ns); |
ac748a09f
|
2581 2582 2583 2584 2585 |
root.mnt = ns->root; root.dentry = ns->root->mnt_root; set_fs_pwd(current->fs, &root); set_fs_root(current->fs, &root); |
1da177e4c
|
2586 |
} |
74bf17cff
|
2587 |
void __init mnt_init(void) |
1da177e4c
|
2588 |
{ |
13f14b4d8
|
2589 |
unsigned u; |
15a67dd8c
|
2590 |
int err; |
1da177e4c
|
2591 |
|
390c68436
|
2592 |
init_rwsem(&namespace_sem); |
1da177e4c
|
2593 |
mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct vfsmount), |
20c2df83d
|
2594 |
0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL); |
1da177e4c
|
2595 |
|
b58fed8b1
|
2596 |
mount_hashtable = (struct list_head *)__get_free_page(GFP_ATOMIC); |
1da177e4c
|
2597 2598 2599 2600 |
if (!mount_hashtable) panic("Failed to allocate mount hash table "); |
80cdc6dae
|
2601 2602 |
printk(KERN_INFO "Mount-cache hash table entries: %lu ", HASH_SIZE); |
13f14b4d8
|
2603 2604 2605 |
for (u = 0; u < HASH_SIZE; u++) INIT_LIST_HEAD(&mount_hashtable[u]); |
1da177e4c
|
2606 |
|
99b7db7b8
|
2607 |
br_lock_init(vfsmount_lock); |
15a67dd8c
|
2608 2609 2610 2611 |
err = sysfs_init(); if (err) printk(KERN_WARNING "%s: sysfs_init error: %d ", |
8e24eea72
|
2612 |
__func__, err); |
00d266662
|
2613 2614 |
fs_kobj = kobject_create_and_add("fs", NULL); if (!fs_kobj) |
8e24eea72
|
2615 2616 |
printk(KERN_WARNING "%s: kobj create error ", __func__); |
1da177e4c
|
2617 2618 2619 |
init_rootfs(); init_mount_tree(); } |
616511d03
|
2620 |
void put_mnt_ns(struct mnt_namespace *ns) |
1da177e4c
|
2621 |
{ |
70fbcdf4d
|
2622 |
LIST_HEAD(umount_list); |
616511d03
|
2623 |
|
d498b25a4
|
2624 |
if (!atomic_dec_and_test(&ns->count)) |
616511d03
|
2625 |
return; |
390c68436
|
2626 |
down_write(&namespace_sem); |
99b7db7b8
|
2627 |
br_write_lock(vfsmount_lock); |
d498b25a4
|
2628 |
umount_tree(ns->root, 0, &umount_list); |
99b7db7b8
|
2629 |
br_write_unlock(vfsmount_lock); |
390c68436
|
2630 |
up_write(&namespace_sem); |
70fbcdf4d
|
2631 |
release_mounts(&umount_list); |
6b3286ed1
|
2632 |
kfree(ns); |
1da177e4c
|
2633 |
} |
9d412a43c
|
2634 2635 2636 |
struct vfsmount *kern_mount_data(struct file_system_type *type, void *data) { |
423e0ab08
|
2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 |
struct vfsmount *mnt; mnt = vfs_kern_mount(type, MS_KERNMOUNT, type->name, data); if (!IS_ERR(mnt)) { /* * it is a longterm mount, don't release mnt until * we unmount before file sys is unregistered */ mnt_make_longterm(mnt); } return mnt; |
9d412a43c
|
2647 2648 |
} EXPORT_SYMBOL_GPL(kern_mount_data); |
423e0ab08
|
2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 |
void kern_unmount(struct vfsmount *mnt) { /* release long term mount so mount point can be released */ if (!IS_ERR_OR_NULL(mnt)) { mnt_make_shortterm(mnt); mntput(mnt); } } EXPORT_SYMBOL(kern_unmount); |
02125a826
|
2659 2660 2661 2662 2663 |
bool our_mnt(struct vfsmount *mnt) { return check_mnt(mnt); } |