Blame view
fs/autofs4/dev-ioctl.c
17.7 KB
8d7b48e0b
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
/* * Copyright 2008 Red Hat, Inc. All rights reserved. * Copyright 2008 Ian Kent <raven@themaw.net> * * This file is part of the Linux kernel and is made available under * the terms of the GNU General Public License, version 2, or at your * option, any later version, incorporated herein by reference. */ #include <linux/module.h> #include <linux/vmalloc.h> #include <linux/miscdevice.h> #include <linux/init.h> #include <linux/wait.h> #include <linux/namei.h> #include <linux/fcntl.h> #include <linux/file.h> #include <linux/fdtable.h> #include <linux/sched.h> #include <linux/compat.h> #include <linux/syscalls.h> |
8d7b48e0b
|
22 23 24 |
#include <linux/magic.h> #include <linux/dcache.h> #include <linux/uaccess.h> |
5a0e3ad6a
|
25 |
#include <linux/slab.h> |
8d7b48e0b
|
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
#include "autofs_i.h" /* * This module implements an interface for routing autofs ioctl control * commands via a miscellaneous device file. * * The alternate interface is needed because we need to be able open * an ioctl file descriptor on an autofs mount that may be covered by * another mount. This situation arises when starting automount(8) * or other user space daemon which uses direct mounts or offset * mounts (used for autofs lazy mount/umount of nested mount trees), * which have been left busy at at service shutdown. */ #define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) typedef int (*ioctl_fn)(struct file *, struct autofs_sb_info *, struct autofs_dev_ioctl *); static int check_name(const char *name) { if (!strchr(name, '/')) return -EINVAL; return 0; } /* * Check a string doesn't overrun the chunk of * memory we copied from user land. */ |
3eac8778a
|
57 |
static int invalid_str(char *str, size_t size) |
8d7b48e0b
|
58 |
{ |
3eac8778a
|
59 60 |
if (memchr(str, 0, size)) return 0; |
8d7b48e0b
|
61 62 63 64 65 66 67 68 69 70 71 72 73 |
return -EINVAL; } /* * Check that the user compiled against correct version of autofs * misc device code. * * As well as checking the version compatibility this always copies * the kernel interface version out. */ static int check_dev_ioctl_version(int cmd, struct autofs_dev_ioctl *param) { int err = 0; |
e9a7c2f1a
|
74 75 |
if ((param->ver_major != AUTOFS_DEV_IOCTL_VERSION_MAJOR) || (param->ver_minor > AUTOFS_DEV_IOCTL_VERSION_MINOR)) { |
8a78d5930
|
76 |
pr_warn("ioctl control interface version mismatch: " |
390855547
|
77 78 |
"kernel(%u.%u), user(%u.%u), cmd(0x%08x) ", |
8a78d5930
|
79 80 81 |
AUTOFS_DEV_IOCTL_VERSION_MAJOR, AUTOFS_DEV_IOCTL_VERSION_MINOR, param->ver_major, param->ver_minor, cmd); |
8d7b48e0b
|
82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
err = -EINVAL; } /* Fill in the kernel version. */ param->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; param->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; return err; } /* * Copy parameter control struct, including a possible path allocated * at the end of the struct. */ |
e9a7c2f1a
|
96 97 |
static struct autofs_dev_ioctl * copy_dev_ioctl(struct autofs_dev_ioctl __user *in) |
8d7b48e0b
|
98 |
{ |
0a280962d
|
99 |
struct autofs_dev_ioctl tmp, *res; |
8d7b48e0b
|
100 101 102 103 104 105 |
if (copy_from_user(&tmp, in, sizeof(tmp))) return ERR_PTR(-EFAULT); if (tmp.size < sizeof(tmp)) return ERR_PTR(-EINVAL); |
e53d77eb8
|
106 107 |
if (tmp.size > (PATH_MAX + sizeof(tmp))) return ERR_PTR(-ENAMETOOLONG); |
0a280962d
|
108 109 110 111 112 |
res = memdup_user(in, tmp.size); if (!IS_ERR(res)) res->size = tmp.size; return res; |
8d7b48e0b
|
113 114 115 116 117 |
} static inline void free_dev_ioctl(struct autofs_dev_ioctl *param) { kfree(param); |
8d7b48e0b
|
118 119 120 121 |
} /* * Check sanity of parameter control fields and if a path is present |
bae8ec665
|
122 |
* check that it is terminated and contains at least one "/". |
8d7b48e0b
|
123 124 125 |
*/ static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param) { |
96b031790
|
126 |
int err; |
8d7b48e0b
|
127 |
|
96b031790
|
128 129 |
err = check_dev_ioctl_version(cmd, param); if (err) { |
8a78d5930
|
130 131 132 |
pr_warn("invalid device control module version " "supplied for cmd(0x%08x) ", cmd); |
8d7b48e0b
|
133 134 135 136 |
goto out; } if (param->size > sizeof(*param)) { |
3eac8778a
|
137 |
err = invalid_str(param->path, param->size - sizeof(*param)); |
8d7b48e0b
|
138 |
if (err) { |
8a78d5930
|
139 |
pr_warn( |
90967c87e
|
140 141 |
"path string terminator missing for cmd(0x%08x) ", |
bae8ec665
|
142 |
cmd); |
8d7b48e0b
|
143 144 |
goto out; } |
bae8ec665
|
145 |
err = check_name(param->path); |
8d7b48e0b
|
146 |
if (err) { |
8a78d5930
|
147 148 149 |
pr_warn("invalid path supplied for cmd(0x%08x) ", cmd); |
8d7b48e0b
|
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
goto out; } } err = 0; out: return err; } /* * Get the autofs super block info struct from the file opened on * the autofs mount point. */ static struct autofs_sb_info *autofs_dev_ioctl_sbi(struct file *f) { struct autofs_sb_info *sbi = NULL; struct inode *inode; if (f) { |
496ad9aa8
|
169 |
inode = file_inode(f); |
8d7b48e0b
|
170 171 172 173 |
sbi = autofs4_sbi(inode->i_sb); } return sbi; } |
d9e192320
|
174 175 176 177 178 179 180 181 182 183 |
/* Return autofs dev ioctl version */ static int autofs_dev_ioctl_version(struct file *fp, struct autofs_sb_info *sbi, struct autofs_dev_ioctl *param) { /* This should have already been set. */ param->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; param->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; return 0; } |
8d7b48e0b
|
184 185 186 187 188 |
/* Return autofs module protocol version */ static int autofs_dev_ioctl_protover(struct file *fp, struct autofs_sb_info *sbi, struct autofs_dev_ioctl *param) { |
730c9eeca
|
189 |
param->protover.version = sbi->version; |
8d7b48e0b
|
190 191 192 193 194 195 196 197 |
return 0; } /* Return autofs module protocol sub version */ static int autofs_dev_ioctl_protosubver(struct file *fp, struct autofs_sb_info *sbi, struct autofs_dev_ioctl *param) { |
730c9eeca
|
198 |
param->protosubver.sub_version = sbi->sub_version; |
8d7b48e0b
|
199 200 |
return 0; } |
ac8387199
|
201 |
/* Find the topmost mount satisfying test() */ |
4e44b6852
|
202 203 204 205 |
static int find_autofs_mount(const char *pathname, struct path *res, int test(struct path *path, void *data), void *data) |
8d7b48e0b
|
206 |
{ |
4e44b6852
|
207 |
struct path path; |
e9a7c2f1a
|
208 209 210 |
int err; err = kern_path_mountpoint(AT_FDCWD, pathname, &path, 0); |
4e44b6852
|
211 212 |
if (err) return err; |
8d7b48e0b
|
213 |
err = -ENOENT; |
4e44b6852
|
214 |
while (path.dentry == path.mnt->mnt_root) { |
d8c9584ea
|
215 |
if (path.dentry->d_sb->s_magic == AUTOFS_SUPER_MAGIC) { |
4e44b6852
|
216 217 |
if (test(&path, data)) { path_get(&path); |
4e44b6852
|
218 |
*res = path; |
8d7b48e0b
|
219 |
err = 0; |
ac8387199
|
220 |
break; |
8d7b48e0b
|
221 222 |
} } |
bab77ebf5
|
223 |
if (!follow_up(&path)) |
4e44b6852
|
224 |
break; |
8d7b48e0b
|
225 |
} |
4e44b6852
|
226 |
path_put(&path); |
8d7b48e0b
|
227 228 |
return err; } |
4e44b6852
|
229 |
static int test_by_dev(struct path *path, void *p) |
8d7b48e0b
|
230 |
{ |
d8c9584ea
|
231 |
return path->dentry->d_sb->s_dev == *(dev_t *)p; |
4e44b6852
|
232 |
} |
8d7b48e0b
|
233 |
|
4e44b6852
|
234 235 236 |
static int test_by_type(struct path *path, void *p) { struct autofs_info *ino = autofs4_dentry_ino(path->dentry); |
e9a7c2f1a
|
237 |
|
4e44b6852
|
238 |
return ino && ino->sbi->type & *(unsigned *)p; |
8d7b48e0b
|
239 |
} |
8d7b48e0b
|
240 241 242 243 |
/* * Open a file descriptor on the autofs mount point corresponding * to the given path and device number (aka. new_encode_dev(sb->s_dev)). */ |
4e44b6852
|
244 |
static int autofs_dev_ioctl_open_mountpoint(const char *name, dev_t devid) |
8d7b48e0b
|
245 |
{ |
8d7b48e0b
|
246 |
int err, fd; |
c921b40d6
|
247 |
fd = get_unused_fd_flags(O_CLOEXEC); |
8d7b48e0b
|
248 |
if (likely(fd >= 0)) { |
4e44b6852
|
249 250 251 252 |
struct file *filp; struct path path; err = find_autofs_mount(name, &path, test_by_dev, &devid); |
8d7b48e0b
|
253 254 255 256 |
if (err) goto out; /* |
4e44b6852
|
257 |
* Find autofs super block that has the device number |
8d7b48e0b
|
258 259 |
* corresponding to the autofs fs we want to open. */ |
8d7b48e0b
|
260 |
|
765927b2d
|
261 262 |
filp = dentry_open(&path, O_RDONLY, current_cred()); path_put(&path); |
8d7b48e0b
|
263 264 265 266 |
if (IS_ERR(filp)) { err = PTR_ERR(filp); goto out; } |
c921b40d6
|
267 |
fd_install(fd, filp); |
8d7b48e0b
|
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
} return fd; out: put_unused_fd(fd); return err; } /* Open a file descriptor on an autofs mount point */ static int autofs_dev_ioctl_openmount(struct file *fp, struct autofs_sb_info *sbi, struct autofs_dev_ioctl *param) { const char *path; dev_t devid; int err, fd; /* param->path has already been checked */ |
730c9eeca
|
287 |
if (!param->openmount.devid) |
8d7b48e0b
|
288 289 290 291 292 |
return -EINVAL; param->ioctlfd = -1; path = param->path; |
4e44b6852
|
293 |
devid = new_decode_dev(param->openmount.devid); |
8d7b48e0b
|
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
err = 0; fd = autofs_dev_ioctl_open_mountpoint(path, devid); if (unlikely(fd < 0)) { err = fd; goto out; } param->ioctlfd = fd; out: return err; } /* Close file descriptor allocated above (user can also use close(2)). */ static int autofs_dev_ioctl_closemount(struct file *fp, struct autofs_sb_info *sbi, struct autofs_dev_ioctl *param) { return sys_close(param->ioctlfd); } /* * Send "ready" status for an existing wait (either a mount or an expire * request). */ static int autofs_dev_ioctl_ready(struct file *fp, struct autofs_sb_info *sbi, struct autofs_dev_ioctl *param) { autofs_wqt_t token; |
730c9eeca
|
324 |
token = (autofs_wqt_t) param->ready.token; |
8d7b48e0b
|
325 326 327 328 329 330 331 332 333 334 335 336 337 |
return autofs4_wait_release(sbi, token, 0); } /* * Send "fail" status for an existing wait (either a mount or an expire * request). */ static int autofs_dev_ioctl_fail(struct file *fp, struct autofs_sb_info *sbi, struct autofs_dev_ioctl *param) { autofs_wqt_t token; int status; |
730c9eeca
|
338 339 |
token = (autofs_wqt_t) param->fail.token; status = param->fail.status ? param->fail.status : -ENOENT; |
8d7b48e0b
|
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 |
return autofs4_wait_release(sbi, token, status); } /* * Set the pipe fd for kernel communication to the daemon. * * Normally this is set at mount using an option but if we * are reconnecting to a busy mount then we need to use this * to tell the autofs mount about the new kernel pipe fd. In * order to protect mounts against incorrectly setting the * pipefd we also require that the autofs mount be catatonic. * * This also sets the process group id used to identify the * controlling process (eg. the owning automount(8) daemon). */ static int autofs_dev_ioctl_setpipefd(struct file *fp, struct autofs_sb_info *sbi, struct autofs_dev_ioctl *param) { int pipefd; int err = 0; |
6eaba35b4
|
361 |
struct pid *new_pid = NULL; |
8d7b48e0b
|
362 |
|
730c9eeca
|
363 |
if (param->setpipefd.pipefd == -1) |
8d7b48e0b
|
364 |
return -EINVAL; |
730c9eeca
|
365 |
pipefd = param->setpipefd.pipefd; |
8d7b48e0b
|
366 367 368 369 370 371 |
mutex_lock(&sbi->wq_mutex); if (!sbi->catatonic) { mutex_unlock(&sbi->wq_mutex); return -EBUSY; } else { |
6eaba35b4
|
372 373 374 375 376 |
struct file *pipe; new_pid = get_task_pid(current, PIDTYPE_PGID); if (ns_of_pid(new_pid) != ns_of_pid(sbi->oz_pgrp)) { |
8a78d5930
|
377 378 |
pr_warn("not allowed to change PID namespace "); |
6eaba35b4
|
379 380 381 382 383 |
err = -EINVAL; goto out; } pipe = fget(pipefd); |
3dc8fe4dc
|
384 385 386 387 |
if (!pipe) { err = -EBADF; goto out; } |
64f371bc3
|
388 |
if (autofs_prepare_pipe(pipe) < 0) { |
8d7b48e0b
|
389 390 391 392 |
err = -EPIPE; fput(pipe); goto out; } |
6eaba35b4
|
393 |
swap(sbi->oz_pgrp, new_pid); |
8d7b48e0b
|
394 395 396 397 398 |
sbi->pipefd = pipefd; sbi->pipe = pipe; sbi->catatonic = 0; } out: |
6eaba35b4
|
399 |
put_pid(new_pid); |
8d7b48e0b
|
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 |
mutex_unlock(&sbi->wq_mutex); return err; } /* * Make the autofs mount point catatonic, no longer responsive to * mount requests. Also closes the kernel pipe file descriptor. */ static int autofs_dev_ioctl_catatonic(struct file *fp, struct autofs_sb_info *sbi, struct autofs_dev_ioctl *param) { autofs4_catatonic_mode(sbi); return 0; } /* Set the autofs mount timeout */ static int autofs_dev_ioctl_timeout(struct file *fp, struct autofs_sb_info *sbi, struct autofs_dev_ioctl *param) { unsigned long timeout; |
730c9eeca
|
422 423 |
timeout = param->timeout.timeout; param->timeout.timeout = sbi->exp_timeout / HZ; |
8d7b48e0b
|
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 |
sbi->exp_timeout = timeout * HZ; return 0; } /* * Return the uid and gid of the last request for the mount * * When reconstructing an autofs mount tree with active mounts * we need to re-connect to mounts that may have used the original * process uid and gid (or string variations of them) for mount * lookups within the map entry. */ static int autofs_dev_ioctl_requester(struct file *fp, struct autofs_sb_info *sbi, struct autofs_dev_ioctl *param) { struct autofs_info *ino; |
4e44b6852
|
441 |
struct path path; |
8d7b48e0b
|
442 443 444 445 446 447 448 |
dev_t devid; int err = -ENOENT; if (param->size <= sizeof(*param)) { err = -EINVAL; goto out; } |
4e44b6852
|
449 |
devid = sbi->sb->s_dev; |
8d7b48e0b
|
450 |
|
730c9eeca
|
451 |
param->requester.uid = param->requester.gid = -1; |
8d7b48e0b
|
452 |
|
4e44b6852
|
453 |
err = find_autofs_mount(param->path, &path, test_by_dev, &devid); |
8d7b48e0b
|
454 455 |
if (err) goto out; |
4e44b6852
|
456 |
ino = autofs4_dentry_ino(path.dentry); |
8d7b48e0b
|
457 458 |
if (ino) { err = 0; |
23bfc2a24
|
459 |
autofs4_expire_wait(path.dentry, 0); |
8d7b48e0b
|
460 |
spin_lock(&sbi->fs_lock); |
e9a7c2f1a
|
461 462 463 464 |
param->requester.uid = from_kuid_munged(current_user_ns(), ino->uid); param->requester.gid = from_kgid_munged(current_user_ns(), ino->gid); |
8d7b48e0b
|
465 466 |
spin_unlock(&sbi->fs_lock); } |
4e44b6852
|
467 |
path_put(&path); |
8d7b48e0b
|
468 469 470 471 472 473 474 475 476 477 478 479 |
out: return err; } /* * Call repeatedly until it returns -EAGAIN, meaning there's nothing * more that can be done. */ static int autofs_dev_ioctl_expire(struct file *fp, struct autofs_sb_info *sbi, struct autofs_dev_ioctl *param) { |
8d7b48e0b
|
480 |
struct vfsmount *mnt; |
8d7b48e0b
|
481 |
int how; |
730c9eeca
|
482 |
how = param->expire.how; |
8d7b48e0b
|
483 |
mnt = fp->f_path.mnt; |
56fcef751
|
484 |
return autofs4_do_expire_multi(sbi->sb, mnt, sbi, how); |
8d7b48e0b
|
485 486 487 488 489 490 491 |
} /* Check if autofs mount point is in use */ static int autofs_dev_ioctl_askumount(struct file *fp, struct autofs_sb_info *sbi, struct autofs_dev_ioctl *param) { |
730c9eeca
|
492 |
param->askumount.may_umount = 0; |
8d7b48e0b
|
493 |
if (may_umount(fp->f_path.mnt)) |
730c9eeca
|
494 |
param->askumount.may_umount = 1; |
8d7b48e0b
|
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 |
return 0; } /* * Check if the given path is a mountpoint. * * If we are supplied with the file descriptor of an autofs * mount we're looking for a specific mount. In this case * the path is considered a mountpoint if it is itself a * mountpoint or contains a mount, such as a multi-mount * without a root mount. In this case we return 1 if the * path is a mount point and the super magic of the covering * mount if there is one or 0 if it isn't a mountpoint. * * If we aren't supplied with a file descriptor then we |
ac8387199
|
510 511 512 513 514 |
* lookup the path and check if it is the root of a mount. * If a type is given we are looking for a particular autofs * mount and if we don't find a match we return fail. If the * located path is the root of a mount we return 1 along with * the super magic of the mount or 0 otherwise. |
8d7b48e0b
|
515 516 517 518 519 520 521 522 |
* * In both cases the the device number (as returned by * new_encode_dev()) is also returned. */ static int autofs_dev_ioctl_ismountpoint(struct file *fp, struct autofs_sb_info *sbi, struct autofs_dev_ioctl *param) { |
4e44b6852
|
523 524 |
struct path path; const char *name; |
8d7b48e0b
|
525 |
unsigned int type; |
730c9eeca
|
526 |
unsigned int devid, magic; |
8d7b48e0b
|
527 528 529 530 531 532 |
int err = -ENOENT; if (param->size <= sizeof(*param)) { err = -EINVAL; goto out; } |
4e44b6852
|
533 |
name = param->path; |
730c9eeca
|
534 |
type = param->ismountpoint.in.type; |
8d7b48e0b
|
535 |
|
730c9eeca
|
536 537 |
param->ismountpoint.out.devid = devid = 0; param->ismountpoint.out.magic = magic = 0; |
8d7b48e0b
|
538 539 |
if (!fp || param->ioctlfd == -1) { |
4e44b6852
|
540 |
if (autofs_type_any(type)) |
ac8387199
|
541 542 |
err = kern_path_mountpoint(AT_FDCWD, name, &path, LOOKUP_FOLLOW); |
4e44b6852
|
543 |
else |
ac8387199
|
544 545 |
err = find_autofs_mount(name, &path, test_by_type, &type); |
4e44b6852
|
546 547 |
if (err) goto out; |
d8c9584ea
|
548 |
devid = new_encode_dev(path.dentry->d_sb->s_dev); |
8d7b48e0b
|
549 |
err = 0; |
f598f9f12
|
550 |
if (path.mnt->mnt_root == path.dentry) { |
8d7b48e0b
|
551 |
err = 1; |
d8c9584ea
|
552 |
magic = path.dentry->d_sb->s_magic; |
8d7b48e0b
|
553 554 |
} } else { |
4e44b6852
|
555 |
dev_t dev = sbi->sb->s_dev; |
8d7b48e0b
|
556 |
|
4e44b6852
|
557 |
err = find_autofs_mount(name, &path, test_by_dev, &dev); |
8d7b48e0b
|
558 559 |
if (err) goto out; |
4e44b6852
|
560 |
devid = new_encode_dev(dev); |
8d7b48e0b
|
561 |
|
4e44b6852
|
562 |
err = have_submounts(path.dentry); |
8d7b48e0b
|
563 |
|
cc53ce53c
|
564 |
if (follow_down_one(&path)) |
d8c9584ea
|
565 |
magic = path.dentry->d_sb->s_magic; |
8d7b48e0b
|
566 |
} |
730c9eeca
|
567 568 |
param->ismountpoint.out.devid = devid; param->ismountpoint.out.magic = magic; |
4e44b6852
|
569 |
path_put(&path); |
8d7b48e0b
|
570 571 572 573 574 575 576 577 578 579 580 581 582 |
out: return err; } /* * Our range of ioctl numbers isn't 0 based so we need to shift * the array index by _IOC_NR(AUTOFS_CTL_IOC_FIRST) for the table * lookup. */ #define cmd_idx(cmd) (cmd - _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST)) static ioctl_fn lookup_dev_ioctl(unsigned int cmd) { |
fcc24534b
|
583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 |
static ioctl_fn _ioctls[] = { autofs_dev_ioctl_version, autofs_dev_ioctl_protover, autofs_dev_ioctl_protosubver, autofs_dev_ioctl_openmount, autofs_dev_ioctl_closemount, autofs_dev_ioctl_ready, autofs_dev_ioctl_fail, autofs_dev_ioctl_setpipefd, autofs_dev_ioctl_catatonic, autofs_dev_ioctl_timeout, autofs_dev_ioctl_requester, autofs_dev_ioctl_expire, autofs_dev_ioctl_askumount, autofs_dev_ioctl_ismountpoint, |
8d7b48e0b
|
598 599 |
}; unsigned int idx = cmd_idx(cmd); |
fcc24534b
|
600 |
return (idx >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[idx]; |
8d7b48e0b
|
601 602 603 |
} /* ioctl dispatcher */ |
e9a7c2f1a
|
604 605 |
static int _autofs_dev_ioctl(unsigned int command, struct autofs_dev_ioctl __user *user) |
8d7b48e0b
|
606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 |
{ struct autofs_dev_ioctl *param; struct file *fp; struct autofs_sb_info *sbi; unsigned int cmd_first, cmd; ioctl_fn fn = NULL; int err = 0; /* only root can play with this */ if (!capable(CAP_SYS_ADMIN)) return -EPERM; cmd_first = _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST); cmd = _IOC_NR(command); if (_IOC_TYPE(command) != _IOC_TYPE(AUTOFS_DEV_IOCTL_IOC_FIRST) || |
aa8419367
|
622 |
cmd - cmd_first > AUTOFS_DEV_IOCTL_IOC_COUNT) { |
8d7b48e0b
|
623 624 625 626 627 628 629 630 631 632 633 |
return -ENOTTY; } /* Copy the parameters into kernel space. */ param = copy_dev_ioctl(user); if (IS_ERR(param)) return PTR_ERR(param); err = validate_dev_ioctl(command, param); if (err) goto out; |
8d7b48e0b
|
634 635 |
fn = lookup_dev_ioctl(cmd); if (!fn) { |
8a78d5930
|
636 637 |
pr_warn("unknown command 0x%08x ", command); |
41a4497a4
|
638 639 |
err = -ENOTTY; goto out; |
8d7b48e0b
|
640 641 642 643 644 645 646 647 |
} fp = NULL; sbi = NULL; /* * For obvious reasons the openmount can't have a file * descriptor yet. We don't take a reference to the |
d9e192320
|
648 649 |
* file during close to allow for immediate release, * and the same for retrieving ioctl version. |
8d7b48e0b
|
650 |
*/ |
d9e192320
|
651 652 |
if (cmd != AUTOFS_DEV_IOCTL_VERSION_CMD && cmd != AUTOFS_DEV_IOCTL_OPENMOUNT_CMD && |
8d7b48e0b
|
653 654 655 656 657 658 659 660 |
cmd != AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD) { fp = fget(param->ioctlfd); if (!fp) { if (cmd == AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD) goto cont; err = -EBADF; goto out; } |
8d7b48e0b
|
661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 |
sbi = autofs_dev_ioctl_sbi(fp); if (!sbi || sbi->magic != AUTOFS_SBI_MAGIC) { err = -EINVAL; fput(fp); goto out; } /* * Admin needs to be able to set the mount catatonic in * order to be able to perform the re-open. */ if (!autofs4_oz_mode(sbi) && cmd != AUTOFS_DEV_IOCTL_CATATONIC_CMD) { err = -EACCES; fput(fp); goto out; } } cont: err = fn(fp, sbi, param); if (fp) fput(fp); |
8d7b48e0b
|
684 685 686 687 688 689 690 691 692 693 |
if (err >= 0 && copy_to_user(user, param, AUTOFS_DEV_IOCTL_SIZE)) err = -EFAULT; out: free_dev_ioctl(param); return err; } static long autofs_dev_ioctl(struct file *file, uint command, ulong u) { int err; |
e9a7c2f1a
|
694 |
|
8d7b48e0b
|
695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 |
err = _autofs_dev_ioctl(command, (struct autofs_dev_ioctl __user *) u); return (long) err; } #ifdef CONFIG_COMPAT static long autofs_dev_ioctl_compat(struct file *file, uint command, ulong u) { return (long) autofs_dev_ioctl(file, command, (ulong) compat_ptr(u)); } #else #define autofs_dev_ioctl_compat NULL #endif static const struct file_operations _dev_ioctl_fops = { .unlocked_ioctl = autofs_dev_ioctl, .compat_ioctl = autofs_dev_ioctl_compat, .owner = THIS_MODULE, |
6038f373a
|
712 |
.llseek = noop_llseek, |
8d7b48e0b
|
713 714 715 |
}; static struct miscdevice _autofs_dev_ioctl_misc = { |
578454ff7
|
716 |
.minor = AUTOFS_MINOR, |
e9a7c2f1a
|
717 718 |
.name = AUTOFS_DEVICE_NAME, .fops = &_dev_ioctl_fops |
8d7b48e0b
|
719 |
}; |
578454ff7
|
720 721 |
MODULE_ALIAS_MISCDEV(AUTOFS_MINOR); MODULE_ALIAS("devname:autofs"); |
8d7b48e0b
|
722 |
/* Register/deregister misc character device */ |
3ff6db328
|
723 |
int __init autofs_dev_ioctl_init(void) |
8d7b48e0b
|
724 725 726 727 728 |
{ int r; r = misc_register(&_autofs_dev_ioctl_misc); if (r) { |
8a78d5930
|
729 730 |
pr_err("misc_register failed for control device "); |
8d7b48e0b
|
731 732 733 734 735 736 737 738 739 |
return r; } return 0; } void autofs_dev_ioctl_exit(void) { misc_deregister(&_autofs_dev_ioctl_misc); |
8d7b48e0b
|
740 |
} |