Blame view
fs/binfmt_misc.c
18.5 KB
09c434b8a
|
1 |
// SPDX-License-Identifier: GPL-2.0-only |
1da177e4c
|
2 |
/* |
e6084d4a0
|
3 |
* binfmt_misc.c |
1da177e4c
|
4 |
* |
e6084d4a0
|
5 |
* Copyright (C) 1997 Richard Günther |
1da177e4c
|
6 |
* |
e6084d4a0
|
7 |
* binfmt_misc detects binaries via a magic or filename extension and invokes |
34962fb80
|
8 |
* a specified wrapper. See Documentation/admin-guide/binfmt-misc.rst for more details. |
1da177e4c
|
9 |
*/ |
6b899c4e9
|
10 |
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
6ceafb880
|
11 |
#include <linux/kernel.h> |
1da177e4c
|
12 13 |
#include <linux/module.h> #include <linux/init.h> |
589ee6284
|
14 |
#include <linux/sched/mm.h> |
b502bd115
|
15 |
#include <linux/magic.h> |
1da177e4c
|
16 17 18 |
#include <linux/binfmts.h> #include <linux/slab.h> #include <linux/ctype.h> |
8d82e180b
|
19 |
#include <linux/string_helpers.h> |
1da177e4c
|
20 21 22 23 |
#include <linux/file.h> #include <linux/pagemap.h> #include <linux/namei.h> #include <linux/mount.h> |
bc99a664e
|
24 |
#include <linux/fs_context.h> |
1da177e4c
|
25 |
#include <linux/syscalls.h> |
6e2c10a12
|
26 |
#include <linux/fs.h> |
6b899c4e9
|
27 |
#include <linux/uaccess.h> |
1da177e4c
|
28 |
|
948b701a6
|
29 |
#include "internal.h" |
6b899c4e9
|
30 31 32 33 34 |
#ifdef DEBUG # define USE_DEBUG 1 #else # define USE_DEBUG 0 #endif |
1da177e4c
|
35 36 37 38 39 40 41 42 43 |
enum { VERBOSE_STATUS = 1 /* make it zero to save 400 bytes kernel memory */ }; static LIST_HEAD(entries); static int enabled = 1; enum {Enabled, Magic}; |
e6084d4a0
|
44 45 46 |
#define MISC_FMT_PRESERVE_ARGV0 (1 << 31) #define MISC_FMT_OPEN_BINARY (1 << 30) #define MISC_FMT_CREDENTIALS (1 << 29) |
948b701a6
|
47 |
#define MISC_FMT_OPEN_FILE (1 << 28) |
1da177e4c
|
48 49 50 51 52 53 54 55 |
typedef struct { struct list_head list; unsigned long flags; /* type, status, etc. */ int offset; /* offset of magic */ int size; /* size of magic/mask */ char *magic; /* magic or filename extension */ char *mask; /* mask, NULL for exact match */ |
50097f749
|
56 |
const char *interpreter; /* filename of interpreter */ |
1da177e4c
|
57 58 |
char *name; struct dentry *dentry; |
948b701a6
|
59 |
struct file *interp_file; |
1da177e4c
|
60 61 62 |
} Node; static DEFINE_RWLOCK(entries_lock); |
1f5ce9e93
|
63 |
static struct file_system_type bm_fs_type; |
1da177e4c
|
64 65 |
static struct vfsmount *bm_mnt; static int entry_count; |
bbaecc088
|
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
/* * Max length of the register string. Determined by: * - 7 delimiters * - name: ~50 bytes * - type: 1 byte * - offset: 3 bytes (has to be smaller than BINPRM_BUF_SIZE) * - magic: 128 bytes (512 in escaped form) * - mask: 128 bytes (512 in escaped form) * - interp: ~50 bytes * - flags: 5 bytes * Round that up a bit, and then back off to hold the internal data * (like struct Node). */ #define MAX_REGISTER_LENGTH 1920 /* |
1da177e4c
|
82 83 84 85 86 87 88 89 |
* Check if we support the binfmt * if we do, return the node, else NULL * locking is done in load_misc_binary */ static Node *check_file(struct linux_binprm *bprm) { char *p = strrchr(bprm->interp, '.'); struct list_head *l; |
6b899c4e9
|
90 |
/* Walk all the registered handlers. */ |
1da177e4c
|
91 92 93 94 |
list_for_each(l, &entries) { Node *e = list_entry(l, Node, list); char *s; int j; |
6b899c4e9
|
95 |
/* Make sure this one is currently enabled. */ |
1da177e4c
|
96 97 |
if (!test_bit(Enabled, &e->flags)) continue; |
6b899c4e9
|
98 |
/* Do matching based on extension if applicable. */ |
1da177e4c
|
99 100 101 102 103 |
if (!test_bit(Magic, &e->flags)) { if (p && !strcmp(e->magic, p + 1)) return e; continue; } |
6b899c4e9
|
104 |
/* Do matching based on magic & mask. */ |
1da177e4c
|
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
s = bprm->buf + e->offset; if (e->mask) { for (j = 0; j < e->size; j++) if ((*s++ ^ e->magic[j]) & e->mask[j]) break; } else { for (j = 0; j < e->size; j++) if ((*s++ ^ e->magic[j])) break; } if (j == e->size) return e; } return NULL; } /* * the loader itself */ |
71613c3b8
|
124 |
static int load_misc_binary(struct linux_binprm *bprm) |
1da177e4c
|
125 126 |
{ Node *fmt; |
e6084d4a0
|
127 |
struct file *interp_file = NULL; |
1da177e4c
|
128 129 |
int retval; int fd_binary = -1; |
1da177e4c
|
130 131 132 |
retval = -ENOEXEC; if (!enabled) |
43a4f2619
|
133 |
return retval; |
1da177e4c
|
134 135 136 137 |
/* to keep locking time low, we copy the interpreter string */ read_lock(&entries_lock); fmt = check_file(bprm); |
50097f749
|
138 |
if (fmt) |
43a4f2619
|
139 |
dget(fmt->dentry); |
1da177e4c
|
140 141 |
read_unlock(&entries_lock); if (!fmt) |
43a4f2619
|
142 |
return retval; |
1da177e4c
|
143 |
|
51f39a1f0
|
144 |
/* Need to be able to load the file after exec */ |
43a4f2619
|
145 |
retval = -ENOENT; |
51f39a1f0
|
146 |
if (bprm->interp_flags & BINPRM_FLAGS_PATH_INACCESSIBLE) |
43a4f2619
|
147 |
goto ret; |
51f39a1f0
|
148 |
|
1da177e4c
|
149 |
if (!(fmt->flags & MISC_FMT_PRESERVE_ARGV0)) { |
b6a2fea39
|
150 151 |
retval = remove_arg_zero(bprm); if (retval) |
e6084d4a0
|
152 |
goto ret; |
1da177e4c
|
153 154 155 |
} if (fmt->flags & MISC_FMT_OPEN_BINARY) { |
1da177e4c
|
156 157 |
/* if the binary should be opened on behalf of the * interpreter than keep it open and assign descriptor |
e6084d4a0
|
158 159 |
* to it */ |
c6cb898b5
|
160 |
fd_binary = get_unused_fd_flags(0); |
e6084d4a0
|
161 162 163 164 165 |
if (fd_binary < 0) { retval = fd_binary; goto ret; } fd_install(fd_binary, bprm->file); |
1da177e4c
|
166 167 168 |
/* if the binary is not readable than enforce mm->dumpable=0 regardless of the interpreter's permissions */ |
1b5d783c9
|
169 |
would_dump(bprm, bprm->file); |
1da177e4c
|
170 171 172 173 174 175 176 |
allow_write_access(bprm->file); bprm->file = NULL; /* mark the bprm that fd should be passed to interp */ bprm->interp_flags |= BINPRM_FLAGS_EXECFD; bprm->interp_data = fd_binary; |
e6084d4a0
|
177 178 179 180 181 |
} else { allow_write_access(bprm->file); fput(bprm->file); bprm->file = NULL; } |
1da177e4c
|
182 |
/* make argv[1] be the path to the binary */ |
e6084d4a0
|
183 |
retval = copy_strings_kernel(1, &bprm->interp, bprm); |
1da177e4c
|
184 |
if (retval < 0) |
e6084d4a0
|
185 |
goto error; |
1da177e4c
|
186 187 188 |
bprm->argc++; /* add the interp as argv[0] */ |
50097f749
|
189 |
retval = copy_strings_kernel(1, &fmt->interpreter, bprm); |
1da177e4c
|
190 |
if (retval < 0) |
e6084d4a0
|
191 192 |
goto error; bprm->argc++; |
1da177e4c
|
193 |
|
b66c59840
|
194 |
/* Update interp in case binfmt_script needs it. */ |
50097f749
|
195 |
retval = bprm_change_interp(fmt->interpreter, bprm); |
b66c59840
|
196 |
if (retval < 0) |
e6084d4a0
|
197 |
goto error; |
1da177e4c
|
198 |
|
eb23aa031
|
199 |
if (fmt->flags & MISC_FMT_OPEN_FILE) { |
19f391eb0
|
200 |
interp_file = file_clone_open(fmt->interp_file); |
948b701a6
|
201 202 203 |
if (!IS_ERR(interp_file)) deny_write_access(interp_file); } else { |
50097f749
|
204 |
interp_file = open_exec(fmt->interpreter); |
948b701a6
|
205 |
} |
e6084d4a0
|
206 207 208 |
retval = PTR_ERR(interp_file); if (IS_ERR(interp_file)) goto error; |
1da177e4c
|
209 210 211 |
bprm->file = interp_file; if (fmt->flags & MISC_FMT_CREDENTIALS) { |
bdd1d2d3d
|
212 |
loff_t pos = 0; |
1da177e4c
|
213 214 215 216 217 |
/* * No need to call prepare_binprm(), it's already been * done. bprm->buf is stale, update from interp_file. */ memset(bprm->buf, 0, BINPRM_BUF_SIZE); |
bdd1d2d3d
|
218 219 |
retval = kernel_read(bprm->file, bprm->buf, BINPRM_BUF_SIZE, &pos); |
1da177e4c
|
220 |
} else |
e6084d4a0
|
221 |
retval = prepare_binprm(bprm); |
1da177e4c
|
222 223 |
if (retval < 0) |
e6084d4a0
|
224 |
goto error; |
1da177e4c
|
225 |
|
3c456bfc4
|
226 |
retval = search_binary_handler(bprm); |
1da177e4c
|
227 |
if (retval < 0) |
e6084d4a0
|
228 |
goto error; |
1da177e4c
|
229 |
|
e6084d4a0
|
230 |
ret: |
43a4f2619
|
231 |
dput(fmt->dentry); |
1da177e4c
|
232 |
return retval; |
e6084d4a0
|
233 |
error: |
1da177e4c
|
234 |
if (fd_binary > 0) |
2ca2a09d6
|
235 |
ksys_close(fd_binary); |
1da177e4c
|
236 237 |
bprm->interp_flags = 0; bprm->interp_data = 0; |
e6084d4a0
|
238 |
goto ret; |
1da177e4c
|
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 |
} /* Command parsers */ /* * parses and copies one argument enclosed in del from *sp to *dp, * recognising the \x special. * returns pointer to the copied argument or NULL in case of an * error (and sets err) or null argument length. */ static char *scanarg(char *s, char del) { char c; while ((c = *s++) != del) { if (c == '\\' && *s == 'x') { s++; if (!isxdigit(*s++)) return NULL; if (!isxdigit(*s++)) return NULL; } } |
7d65cf10e
|
262 |
s[-1] ='\0'; |
1da177e4c
|
263 264 |
return s; } |
e6084d4a0
|
265 |
static char *check_special_flags(char *sfs, Node *e) |
1da177e4c
|
266 |
{ |
e6084d4a0
|
267 |
char *p = sfs; |
1da177e4c
|
268 269 270 271 272 |
int cont = 1; /* special flags */ while (cont) { switch (*p) { |
e6084d4a0
|
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 |
case 'P': pr_debug("register: flag: P (preserve argv0) "); p++; e->flags |= MISC_FMT_PRESERVE_ARGV0; break; case 'O': pr_debug("register: flag: O (open binary) "); p++; e->flags |= MISC_FMT_OPEN_BINARY; break; case 'C': pr_debug("register: flag: C (preserve creds) "); p++; /* this flags also implies the open-binary flag */ e->flags |= (MISC_FMT_CREDENTIALS | MISC_FMT_OPEN_BINARY); break; |
948b701a6
|
294 295 296 297 298 299 |
case 'F': pr_debug("register: flag: F: open interpreter file now "); p++; e->flags |= MISC_FMT_OPEN_FILE; break; |
e6084d4a0
|
300 301 |
default: cont = 0; |
1da177e4c
|
302 303 304 305 306 |
} } return p; } |
e6084d4a0
|
307 |
|
1da177e4c
|
308 309 310 311 312 313 314 315 316 317 318 |
/* * This registers a new binary format, it recognises the syntax * ':name:type:offset:magic:mask:interpreter:flags' * where the ':' is the IFS, that can be chosen with the first char */ static Node *create_entry(const char __user *buffer, size_t count) { Node *e; int memsize, err; char *buf, *p; char del; |
6b899c4e9
|
319 320 |
pr_debug("register: received %zu bytes ", count); |
1da177e4c
|
321 322 |
/* some sanity checks */ err = -EINVAL; |
bbaecc088
|
323 |
if ((count < 11) || (count > MAX_REGISTER_LENGTH)) |
1da177e4c
|
324 325 326 327 |
goto out; err = -ENOMEM; memsize = sizeof(Node) + count + 8; |
f7e1ad1a1
|
328 |
e = kmalloc(memsize, GFP_KERNEL); |
1da177e4c
|
329 330 331 332 333 334 335 |
if (!e) goto out; p = buf = (char *)e + sizeof(Node); memset(e, 0, sizeof(Node)); if (copy_from_user(buf, buffer, count)) |
e6084d4a0
|
336 |
goto efault; |
1da177e4c
|
337 338 |
del = *p++; /* delimeter */ |
6b899c4e9
|
339 340 341 342 |
pr_debug("register: delim: %#x {%c} ", del, del); /* Pad the buffer with the delim to simplify parsing below. */ |
e6084d4a0
|
343 |
memset(buf + count, del, 8); |
1da177e4c
|
344 |
|
6b899c4e9
|
345 |
/* Parse the 'name' field. */ |
1da177e4c
|
346 347 348 |
e->name = p; p = strchr(p, del); if (!p) |
e6084d4a0
|
349 |
goto einval; |
1da177e4c
|
350 351 352 353 354 |
*p++ = '\0'; if (!e->name[0] || !strcmp(e->name, ".") || !strcmp(e->name, "..") || strchr(e->name, '/')) |
e6084d4a0
|
355 |
goto einval; |
6b899c4e9
|
356 357 358 359 360 |
pr_debug("register: name: {%s} ", e->name); /* Parse the 'type' field. */ |
1da177e4c
|
361 |
switch (*p++) { |
6b899c4e9
|
362 363 364 365 366 367 368 369 370 371 372 |
case 'E': pr_debug("register: type: E (extension) "); e->flags = 1 << Enabled; break; case 'M': pr_debug("register: type: M (magic) "); e->flags = (1 << Enabled) | (1 << Magic); break; default: |
e6084d4a0
|
373 |
goto einval; |
1da177e4c
|
374 375 |
} if (*p++ != del) |
e6084d4a0
|
376 |
goto einval; |
6b899c4e9
|
377 |
|
1da177e4c
|
378 |
if (test_bit(Magic, &e->flags)) { |
6b899c4e9
|
379 380 381 382 383 |
/* Handle the 'M' (magic) format. */ char *s; /* Parse the 'offset' field. */ s = strchr(p, del); |
1da177e4c
|
384 |
if (!s) |
e6084d4a0
|
385 |
goto einval; |
5cc41e099
|
386 387 388 389 390 391 392 |
*s = '\0'; if (p != s) { int r = kstrtoint(p, 10, &e->offset); if (r != 0 || e->offset < 0) goto einval; } p = s; |
1da177e4c
|
393 |
if (*p++) |
e6084d4a0
|
394 |
goto einval; |
6b899c4e9
|
395 396 397 398 |
pr_debug("register: offset: %#x ", e->offset); /* Parse the 'magic' field. */ |
1da177e4c
|
399 400 401 |
e->magic = p; p = scanarg(p, del); if (!p) |
e6084d4a0
|
402 |
goto einval; |
7d65cf10e
|
403 |
if (!e->magic[0]) |
e6084d4a0
|
404 |
goto einval; |
6b899c4e9
|
405 406 407 408 409 410 |
if (USE_DEBUG) print_hex_dump_bytes( KBUILD_MODNAME ": register: magic[raw]: ", DUMP_PREFIX_NONE, e->magic, p - e->magic); /* Parse the 'mask' field. */ |
1da177e4c
|
411 412 413 |
e->mask = p; p = scanarg(p, del); if (!p) |
e6084d4a0
|
414 |
goto einval; |
7d65cf10e
|
415 |
if (!e->mask[0]) { |
1da177e4c
|
416 |
e->mask = NULL; |
6b899c4e9
|
417 418 419 420 421 422 423 424 425 426 427 428 429 |
pr_debug("register: mask[raw]: none "); } else if (USE_DEBUG) print_hex_dump_bytes( KBUILD_MODNAME ": register: mask[raw]: ", DUMP_PREFIX_NONE, e->mask, p - e->mask); /* * Decode the magic & mask fields. * Note: while we might have accepted embedded NUL bytes from * above, the unescape helpers here will stop at the first one * it encounters. */ |
8d82e180b
|
430 431 432 |
e->size = string_unescape_inplace(e->magic, UNESCAPE_HEX); if (e->mask && string_unescape_inplace(e->mask, UNESCAPE_HEX) != e->size) |
e6084d4a0
|
433 |
goto einval; |
5cc41e099
|
434 435 |
if (e->size > BINPRM_BUF_SIZE || BINPRM_BUF_SIZE - e->size < e->offset) |
e6084d4a0
|
436 |
goto einval; |
6b899c4e9
|
437 438 439 440 441 442 443 444 445 |
pr_debug("register: magic/mask length: %i ", e->size); if (USE_DEBUG) { print_hex_dump_bytes( KBUILD_MODNAME ": register: magic[decoded]: ", DUMP_PREFIX_NONE, e->magic, e->size); if (e->mask) { int i; |
f7e1ad1a1
|
446 |
char *masked = kmalloc(e->size, GFP_KERNEL); |
6b899c4e9
|
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 |
print_hex_dump_bytes( KBUILD_MODNAME ": register: mask[decoded]: ", DUMP_PREFIX_NONE, e->mask, e->size); if (masked) { for (i = 0; i < e->size; ++i) masked[i] = e->magic[i] & e->mask[i]; print_hex_dump_bytes( KBUILD_MODNAME ": register: magic[masked]: ", DUMP_PREFIX_NONE, masked, e->size); kfree(masked); } } } |
1da177e4c
|
463 |
} else { |
6b899c4e9
|
464 465 466 |
/* Handle the 'E' (extension) format. */ /* Skip the 'offset' field. */ |
1da177e4c
|
467 468 |
p = strchr(p, del); if (!p) |
e6084d4a0
|
469 |
goto einval; |
1da177e4c
|
470 |
*p++ = '\0'; |
6b899c4e9
|
471 472 |
/* Parse the 'magic' field. */ |
1da177e4c
|
473 474 475 |
e->magic = p; p = strchr(p, del); if (!p) |
e6084d4a0
|
476 |
goto einval; |
1da177e4c
|
477 478 |
*p++ = '\0'; if (!e->magic[0] || strchr(e->magic, '/')) |
e6084d4a0
|
479 |
goto einval; |
6b899c4e9
|
480 481 482 483 |
pr_debug("register: extension: {%s} ", e->magic); /* Skip the 'mask' field. */ |
1da177e4c
|
484 485 |
p = strchr(p, del); if (!p) |
e6084d4a0
|
486 |
goto einval; |
1da177e4c
|
487 488 |
*p++ = '\0'; } |
6b899c4e9
|
489 490 |
/* Parse the 'interpreter' field. */ |
1da177e4c
|
491 492 493 |
e->interpreter = p; p = strchr(p, del); if (!p) |
e6084d4a0
|
494 |
goto einval; |
1da177e4c
|
495 496 |
*p++ = '\0'; if (!e->interpreter[0]) |
e6084d4a0
|
497 |
goto einval; |
6b899c4e9
|
498 499 |
pr_debug("register: interpreter: {%s} ", e->interpreter); |
1da177e4c
|
500 |
|
6b899c4e9
|
501 |
/* Parse the 'flags' field. */ |
e6084d4a0
|
502 |
p = check_special_flags(p, e); |
1da177e4c
|
503 504 505 506 |
if (*p == ' ') p++; if (p != buf + count) |
e6084d4a0
|
507 |
goto einval; |
1da177e4c
|
508 509 510 511 |
return e; out: return ERR_PTR(err); |
e6084d4a0
|
512 |
efault: |
1da177e4c
|
513 514 |
kfree(e); return ERR_PTR(-EFAULT); |
e6084d4a0
|
515 |
einval: |
1da177e4c
|
516 517 518 519 520 521 522 523 524 525 526 |
kfree(e); return ERR_PTR(-EINVAL); } /* * Set status of entry/binfmt_misc: * '1' enables, '0' disables and '-1' clears entry/binfmt_misc */ static int parse_command(const char __user *buffer, size_t count) { char s[4]; |
1da177e4c
|
527 528 529 530 |
if (count > 3) return -EINVAL; if (copy_from_user(s, buffer, count)) return -EFAULT; |
de8288b1f
|
531 532 |
if (!count) return 0; |
e6084d4a0
|
533 534 |
if (s[count - 1] == ' ') |
1da177e4c
|
535 536 537 538 539 540 541 542 543 544 545 546 547 548 |
count--; if (count == 1 && s[0] == '0') return 1; if (count == 1 && s[0] == '1') return 2; if (count == 2 && s[0] == '-' && s[1] == '1') return 3; return -EINVAL; } /* generic stuff */ static void entry_status(Node *e, char *page) { |
6ceafb880
|
549 550 |
char *dp = page; const char *status = "disabled"; |
1da177e4c
|
551 552 553 554 555 556 557 558 559 |
if (test_bit(Enabled, &e->flags)) status = "enabled"; if (!VERBOSE_STATUS) { sprintf(page, "%s ", status); return; } |
6ceafb880
|
560 561 562 |
dp += sprintf(dp, "%s interpreter %s ", status, e->interpreter); |
1da177e4c
|
563 564 |
/* print the special flags */ |
6ceafb880
|
565 |
dp += sprintf(dp, "flags: "); |
e6084d4a0
|
566 567 568 569 570 571 |
if (e->flags & MISC_FMT_PRESERVE_ARGV0) *dp++ = 'P'; if (e->flags & MISC_FMT_OPEN_BINARY) *dp++ = 'O'; if (e->flags & MISC_FMT_CREDENTIALS) *dp++ = 'C'; |
948b701a6
|
572 573 |
if (e->flags & MISC_FMT_OPEN_FILE) *dp++ = 'F'; |
e6084d4a0
|
574 575 |
*dp++ = ' '; |
1da177e4c
|
576 577 578 579 580 |
if (!test_bit(Magic, &e->flags)) { sprintf(dp, "extension .%s ", e->magic); } else { |
6ceafb880
|
581 582 583 |
dp += sprintf(dp, "offset %i magic ", e->offset); dp = bin2hex(dp, e->magic, e->size); |
1da177e4c
|
584 |
if (e->mask) { |
6ceafb880
|
585 586 587 |
dp += sprintf(dp, " mask "); dp = bin2hex(dp, e->mask, e->size); |
1da177e4c
|
588 589 590 591 592 593 594 595 596 |
} *dp++ = ' '; *dp = '\0'; } } static struct inode *bm_get_inode(struct super_block *sb, int mode) { |
e6084d4a0
|
597 |
struct inode *inode = new_inode(sb); |
1da177e4c
|
598 599 |
if (inode) { |
85fe4025c
|
600 |
inode->i_ino = get_next_ino(); |
1da177e4c
|
601 |
inode->i_mode = mode; |
1da177e4c
|
602 |
inode->i_atime = inode->i_mtime = inode->i_ctime = |
c2050a454
|
603 |
current_time(inode); |
1da177e4c
|
604 605 606 |
} return inode; } |
b57922d97
|
607 |
static void bm_evict_inode(struct inode *inode) |
1da177e4c
|
608 |
{ |
83f918274
|
609 |
Node *e = inode->i_private; |
7e8660060
|
610 |
if (e && e->flags & MISC_FMT_OPEN_FILE) |
83f918274
|
611 |
filp_close(e->interp_file, NULL); |
dbd5768f8
|
612 |
clear_inode(inode); |
83f918274
|
613 |
kfree(e); |
1da177e4c
|
614 615 616 617 618 619 620 |
} static void kill_node(Node *e) { struct dentry *dentry; write_lock(&entries_lock); |
baba1b297
|
621 |
list_del_init(&e->list); |
1da177e4c
|
622 |
write_unlock(&entries_lock); |
baba1b297
|
623 624 625 626 627 |
dentry = e->dentry; drop_nlink(d_inode(dentry)); d_drop(dentry); dput(dentry); simple_release_fs(&bm_mnt, &entry_count); |
1da177e4c
|
628 629 630 631 632 |
} /* /<entry> */ static ssize_t |
e6084d4a0
|
633 |
bm_entry_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) |
1da177e4c
|
634 |
{ |
496ad9aa8
|
635 |
Node *e = file_inode(file)->i_private; |
1da177e4c
|
636 637 |
ssize_t res; char *page; |
1da177e4c
|
638 |
|
e6084d4a0
|
639 640 |
page = (char *) __get_free_page(GFP_KERNEL); if (!page) |
1da177e4c
|
641 642 643 |
return -ENOMEM; entry_status(e, page); |
1da177e4c
|
644 |
|
6e2c10a12
|
645 |
res = simple_read_from_buffer(buf, nbytes, ppos, page, strlen(page)); |
1da177e4c
|
646 647 648 649 650 651 652 653 |
free_page((unsigned long) page); return res; } static ssize_t bm_entry_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { struct dentry *root; |
496ad9aa8
|
654 |
Node *e = file_inode(file)->i_private; |
1da177e4c
|
655 656 657 |
int res = parse_command(buffer, count); switch (res) { |
e6084d4a0
|
658 659 660 661 662 663 664 665 666 667 |
case 1: /* Disable this handler. */ clear_bit(Enabled, &e->flags); break; case 2: /* Enable this handler. */ set_bit(Enabled, &e->flags); break; case 3: /* Delete this handler. */ |
ea7d4c046
|
668 |
root = file_inode(file)->i_sb->s_root; |
5955102c9
|
669 |
inode_lock(d_inode(root)); |
1da177e4c
|
670 |
|
baba1b297
|
671 672 |
if (!list_empty(&e->list)) kill_node(e); |
1da177e4c
|
673 |
|
5955102c9
|
674 |
inode_unlock(d_inode(root)); |
e6084d4a0
|
675 676 677 |
break; default: return res; |
1da177e4c
|
678 |
} |
e6084d4a0
|
679 |
|
1da177e4c
|
680 681 |
return count; } |
4b6f5d20b
|
682 |
static const struct file_operations bm_entry_operations = { |
1da177e4c
|
683 684 |
.read = bm_entry_read, .write = bm_entry_write, |
6038f373a
|
685 |
.llseek = default_llseek, |
1da177e4c
|
686 687 688 689 690 691 692 693 694 |
}; /* /register */ static ssize_t bm_register_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { Node *e; struct inode *inode; |
ea7d4c046
|
695 696 |
struct super_block *sb = file_inode(file)->i_sb; struct dentry *root = sb->s_root, *dentry; |
1da177e4c
|
697 698 699 700 701 702 |
int err = 0; e = create_entry(buffer, count); if (IS_ERR(e)) return PTR_ERR(e); |
5955102c9
|
703 |
inode_lock(d_inode(root)); |
1da177e4c
|
704 705 706 707 708 709 |
dentry = lookup_one_len(e->name, root, strlen(e->name)); err = PTR_ERR(dentry); if (IS_ERR(dentry)) goto out; err = -EEXIST; |
75c3cfa85
|
710 |
if (d_really_is_positive(dentry)) |
1da177e4c
|
711 712 713 714 715 716 717 |
goto out2; inode = bm_get_inode(sb, S_IFREG | 0644); err = -ENOMEM; if (!inode) goto out2; |
1f5ce9e93
|
718 |
err = simple_pin_fs(&bm_fs_type, &bm_mnt, &entry_count); |
1da177e4c
|
719 720 721 722 723 |
if (err) { iput(inode); inode = NULL; goto out2; } |
948b701a6
|
724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 |
if (e->flags & MISC_FMT_OPEN_FILE) { struct file *f; f = open_exec(e->interpreter); if (IS_ERR(f)) { err = PTR_ERR(f); pr_notice("register: failed to install interpreter file %s ", e->interpreter); simple_release_fs(&bm_mnt, &entry_count); iput(inode); inode = NULL; goto out2; } e->interp_file = f; } |
1da177e4c
|
739 |
e->dentry = dget(dentry); |
8e18e2941
|
740 |
inode->i_private = e; |
1da177e4c
|
741 742 743 744 745 746 747 748 749 750 751 |
inode->i_fop = &bm_entry_operations; d_instantiate(dentry, inode); write_lock(&entries_lock); list_add(&e->list, &entries); write_unlock(&entries_lock); err = 0; out2: dput(dentry); out: |
5955102c9
|
752 |
inode_unlock(d_inode(root)); |
1da177e4c
|
753 754 755 |
if (err) { kfree(e); |
948b701a6
|
756 |
return err; |
1da177e4c
|
757 758 759 |
} return count; } |
4b6f5d20b
|
760 |
static const struct file_operations bm_register_operations = { |
1da177e4c
|
761 |
.write = bm_register_write, |
6038f373a
|
762 |
.llseek = noop_llseek, |
1da177e4c
|
763 764 765 766 767 768 769 |
}; /* /status */ static ssize_t bm_status_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { |
87113e806
|
770 771 772 |
char *s = enabled ? "enabled " : "disabled "; |
1da177e4c
|
773 |
|
92f4c701a
|
774 |
return simple_read_from_buffer(buf, nbytes, ppos, s, strlen(s)); |
1da177e4c
|
775 |
} |
e6084d4a0
|
776 |
static ssize_t bm_status_write(struct file *file, const char __user *buffer, |
1da177e4c
|
777 778 779 780 781 782 |
size_t count, loff_t *ppos) { int res = parse_command(buffer, count); struct dentry *root; switch (res) { |
e6084d4a0
|
783 784 785 786 787 788 789 790 791 792 |
case 1: /* Disable all handlers. */ enabled = 0; break; case 2: /* Enable all handlers. */ enabled = 1; break; case 3: /* Delete all handlers. */ |
ea7d4c046
|
793 |
root = file_inode(file)->i_sb->s_root; |
5955102c9
|
794 |
inode_lock(d_inode(root)); |
1da177e4c
|
795 |
|
e6084d4a0
|
796 |
while (!list_empty(&entries)) |
baba1b297
|
797 |
kill_node(list_first_entry(&entries, Node, list)); |
1da177e4c
|
798 |
|
5955102c9
|
799 |
inode_unlock(d_inode(root)); |
e6084d4a0
|
800 801 802 |
break; default: return res; |
1da177e4c
|
803 |
} |
e6084d4a0
|
804 |
|
1da177e4c
|
805 806 |
return count; } |
4b6f5d20b
|
807 |
static const struct file_operations bm_status_operations = { |
1da177e4c
|
808 809 |
.read = bm_status_read, .write = bm_status_write, |
6038f373a
|
810 |
.llseek = default_llseek, |
1da177e4c
|
811 812 813 |
}; /* Superblock handling */ |
ee9b6d61a
|
814 |
static const struct super_operations s_ops = { |
1da177e4c
|
815 |
.statfs = simple_statfs, |
b57922d97
|
816 |
.evict_inode = bm_evict_inode, |
1da177e4c
|
817 |
}; |
bc99a664e
|
818 |
static int bm_fill_super(struct super_block *sb, struct fs_context *fc) |
1da177e4c
|
819 |
{ |
e6084d4a0
|
820 |
int err; |
cda37124f
|
821 |
static const struct tree_descr bm_files[] = { |
1a1c9bb43
|
822 823 |
[2] = {"status", &bm_status_operations, S_IWUSR|S_IRUGO}, [3] = {"register", &bm_register_operations, S_IWUSR}, |
1da177e4c
|
824 825 |
/* last one */ {""} }; |
e6084d4a0
|
826 827 |
err = simple_fill_super(sb, BINFMTFS_MAGIC, bm_files); |
1da177e4c
|
828 829 830 831 |
if (!err) sb->s_op = &s_ops; return err; } |
bc99a664e
|
832 |
static int bm_get_tree(struct fs_context *fc) |
1da177e4c
|
833 |
{ |
bc99a664e
|
834 835 836 837 838 839 840 841 842 843 844 |
return get_tree_single(fc, bm_fill_super); } static const struct fs_context_operations bm_context_ops = { .get_tree = bm_get_tree, }; static int bm_init_fs_context(struct fs_context *fc) { fc->ops = &bm_context_ops; return 0; |
1da177e4c
|
845 846 847 848 849 850 851 852 853 854 |
} static struct linux_binfmt misc_format = { .module = THIS_MODULE, .load_binary = load_misc_binary, }; static struct file_system_type bm_fs_type = { .owner = THIS_MODULE, .name = "binfmt_misc", |
bc99a664e
|
855 |
.init_fs_context = bm_init_fs_context, |
1da177e4c
|
856 857 |
.kill_sb = kill_litter_super, }; |
7f78e0351
|
858 |
MODULE_ALIAS_FS("binfmt_misc"); |
1da177e4c
|
859 860 861 862 |
static int __init init_misc_binfmt(void) { int err = register_filesystem(&bm_fs_type); |
8fc3dc5a3
|
863 864 |
if (!err) insert_binfmt(&misc_format); |
1da177e4c
|
865 866 867 868 869 870 871 872 873 874 875 876 |
return err; } static void __exit exit_misc_binfmt(void) { unregister_binfmt(&misc_format); unregister_filesystem(&bm_fs_type); } core_initcall(init_misc_binfmt); module_exit(exit_misc_binfmt); MODULE_LICENSE("GPL"); |