| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | #include <linux/init.h> | 
|  | 2 | #include <linux/fs.h> | 
|  | 3 | #include <linux/slab.h> | 
|  | 4 | #include <linux/types.h> | 
|  | 5 | #include <linux/fcntl.h> | 
|  | 6 | #include <linux/delay.h> | 
|  | 7 | #include <linux/string.h> | 
|  | 8 | #include <linux/syscalls.h> | 
|  | 9 |  | 
|  | 10 | static __initdata char *message; | 
|  | 11 | static void __init error(char *x) | 
|  | 12 | { | 
|  | 13 | if (!message) | 
|  | 14 | message = x; | 
|  | 15 | } | 
|  | 16 |  | 
|  | 17 | static void __init *malloc(size_t size) | 
|  | 18 | { | 
|  | 19 | return kmalloc(size, GFP_KERNEL); | 
|  | 20 | } | 
|  | 21 |  | 
|  | 22 | static void __init free(void *where) | 
|  | 23 | { | 
|  | 24 | kfree(where); | 
|  | 25 | } | 
|  | 26 |  | 
|  | 27 | /* link hash */ | 
|  | 28 |  | 
|  | 29 | static __initdata struct hash { | 
|  | 30 | int ino, minor, major; | 
|  | 31 | struct hash *next; | 
|  | 32 | char *name; | 
|  | 33 | } *head[32]; | 
|  | 34 |  | 
|  | 35 | static inline int hash(int major, int minor, int ino) | 
|  | 36 | { | 
|  | 37 | unsigned long tmp = ino + minor + (major << 3); | 
|  | 38 | tmp += tmp >> 5; | 
|  | 39 | return tmp & 31; | 
|  | 40 | } | 
|  | 41 |  | 
|  | 42 | static char __init *find_link(int major, int minor, int ino, char *name) | 
|  | 43 | { | 
|  | 44 | struct hash **p, *q; | 
|  | 45 | for (p = head + hash(major, minor, ino); *p; p = &(*p)->next) { | 
|  | 46 | if ((*p)->ino != ino) | 
|  | 47 | continue; | 
|  | 48 | if ((*p)->minor != minor) | 
|  | 49 | continue; | 
|  | 50 | if ((*p)->major != major) | 
|  | 51 | continue; | 
|  | 52 | return (*p)->name; | 
|  | 53 | } | 
|  | 54 | q = (struct hash *)malloc(sizeof(struct hash)); | 
|  | 55 | if (!q) | 
|  | 56 | panic("can't allocate link hash entry"); | 
|  | 57 | q->ino = ino; | 
|  | 58 | q->minor = minor; | 
|  | 59 | q->major = major; | 
|  | 60 | q->name = name; | 
|  | 61 | q->next = NULL; | 
|  | 62 | *p = q; | 
|  | 63 | return NULL; | 
|  | 64 | } | 
|  | 65 |  | 
|  | 66 | static void __init free_hash(void) | 
|  | 67 | { | 
|  | 68 | struct hash **p, *q; | 
|  | 69 | for (p = head; p < head + 32; p++) { | 
|  | 70 | while (*p) { | 
|  | 71 | q = *p; | 
|  | 72 | *p = q->next; | 
|  | 73 | free(q); | 
|  | 74 | } | 
|  | 75 | } | 
|  | 76 | } | 
|  | 77 |  | 
|  | 78 | /* cpio header parsing */ | 
|  | 79 |  | 
|  | 80 | static __initdata unsigned long ino, major, minor, nlink; | 
|  | 81 | static __initdata mode_t mode; | 
|  | 82 | static __initdata unsigned long body_len, name_len; | 
|  | 83 | static __initdata uid_t uid; | 
|  | 84 | static __initdata gid_t gid; | 
|  | 85 | static __initdata unsigned rdev; | 
|  | 86 |  | 
|  | 87 | static void __init parse_header(char *s) | 
|  | 88 | { | 
|  | 89 | unsigned long parsed[12]; | 
|  | 90 | char buf[9]; | 
|  | 91 | int i; | 
|  | 92 |  | 
|  | 93 | buf[8] = '\0'; | 
|  | 94 | for (i = 0, s += 6; i < 12; i++, s += 8) { | 
|  | 95 | memcpy(buf, s, 8); | 
|  | 96 | parsed[i] = simple_strtoul(buf, NULL, 16); | 
|  | 97 | } | 
|  | 98 | ino = parsed[0]; | 
|  | 99 | mode = parsed[1]; | 
|  | 100 | uid = parsed[2]; | 
|  | 101 | gid = parsed[3]; | 
|  | 102 | nlink = parsed[4]; | 
|  | 103 | body_len = parsed[6]; | 
|  | 104 | major = parsed[7]; | 
|  | 105 | minor = parsed[8]; | 
|  | 106 | rdev = new_encode_dev(MKDEV(parsed[9], parsed[10])); | 
|  | 107 | name_len = parsed[11]; | 
|  | 108 | } | 
|  | 109 |  | 
|  | 110 | /* FSM */ | 
|  | 111 |  | 
|  | 112 | static __initdata enum state { | 
|  | 113 | Start, | 
|  | 114 | Collect, | 
|  | 115 | GotHeader, | 
|  | 116 | SkipIt, | 
|  | 117 | GotName, | 
|  | 118 | CopyFile, | 
|  | 119 | GotSymlink, | 
|  | 120 | Reset | 
|  | 121 | } state, next_state; | 
|  | 122 |  | 
|  | 123 | static __initdata char *victim; | 
|  | 124 | static __initdata unsigned count; | 
|  | 125 | static __initdata loff_t this_header, next_header; | 
|  | 126 |  | 
|  | 127 | static __initdata int dry_run; | 
|  | 128 |  | 
|  | 129 | static inline void eat(unsigned n) | 
|  | 130 | { | 
|  | 131 | victim += n; | 
|  | 132 | this_header += n; | 
|  | 133 | count -= n; | 
|  | 134 | } | 
|  | 135 |  | 
|  | 136 | #define N_ALIGN(len) ((((len) + 1) & ~3) + 2) | 
|  | 137 |  | 
|  | 138 | static __initdata char *collected; | 
|  | 139 | static __initdata int remains; | 
|  | 140 | static __initdata char *collect; | 
|  | 141 |  | 
|  | 142 | static void __init read_into(char *buf, unsigned size, enum state next) | 
|  | 143 | { | 
|  | 144 | if (count >= size) { | 
|  | 145 | collected = victim; | 
|  | 146 | eat(size); | 
|  | 147 | state = next; | 
|  | 148 | } else { | 
|  | 149 | collect = collected = buf; | 
|  | 150 | remains = size; | 
|  | 151 | next_state = next; | 
|  | 152 | state = Collect; | 
|  | 153 | } | 
|  | 154 | } | 
|  | 155 |  | 
|  | 156 | static __initdata char *header_buf, *symlink_buf, *name_buf; | 
|  | 157 |  | 
|  | 158 | static int __init do_start(void) | 
|  | 159 | { | 
|  | 160 | read_into(header_buf, 110, GotHeader); | 
|  | 161 | return 0; | 
|  | 162 | } | 
|  | 163 |  | 
|  | 164 | static int __init do_collect(void) | 
|  | 165 | { | 
|  | 166 | unsigned n = remains; | 
|  | 167 | if (count < n) | 
|  | 168 | n = count; | 
|  | 169 | memcpy(collect, victim, n); | 
|  | 170 | eat(n); | 
|  | 171 | collect += n; | 
|  | 172 | if ((remains -= n) != 0) | 
|  | 173 | return 1; | 
|  | 174 | state = next_state; | 
|  | 175 | return 0; | 
|  | 176 | } | 
|  | 177 |  | 
|  | 178 | static int __init do_header(void) | 
|  | 179 | { | 
|  | 180 | if (memcmp(collected, "070701", 6)) { | 
|  | 181 | error("no cpio magic"); | 
|  | 182 | return 1; | 
|  | 183 | } | 
|  | 184 | parse_header(collected); | 
|  | 185 | next_header = this_header + N_ALIGN(name_len) + body_len; | 
|  | 186 | next_header = (next_header + 3) & ~3; | 
|  | 187 | if (dry_run) { | 
|  | 188 | read_into(name_buf, N_ALIGN(name_len), GotName); | 
|  | 189 | return 0; | 
|  | 190 | } | 
|  | 191 | state = SkipIt; | 
|  | 192 | if (name_len <= 0 || name_len > PATH_MAX) | 
|  | 193 | return 0; | 
|  | 194 | if (S_ISLNK(mode)) { | 
|  | 195 | if (body_len > PATH_MAX) | 
|  | 196 | return 0; | 
|  | 197 | collect = collected = symlink_buf; | 
|  | 198 | remains = N_ALIGN(name_len) + body_len; | 
|  | 199 | next_state = GotSymlink; | 
|  | 200 | state = Collect; | 
|  | 201 | return 0; | 
|  | 202 | } | 
|  | 203 | if (S_ISREG(mode) || !body_len) | 
|  | 204 | read_into(name_buf, N_ALIGN(name_len), GotName); | 
|  | 205 | return 0; | 
|  | 206 | } | 
|  | 207 |  | 
|  | 208 | static int __init do_skip(void) | 
|  | 209 | { | 
|  | 210 | if (this_header + count < next_header) { | 
|  | 211 | eat(count); | 
|  | 212 | return 1; | 
|  | 213 | } else { | 
|  | 214 | eat(next_header - this_header); | 
|  | 215 | state = next_state; | 
|  | 216 | return 0; | 
|  | 217 | } | 
|  | 218 | } | 
|  | 219 |  | 
|  | 220 | static int __init do_reset(void) | 
|  | 221 | { | 
|  | 222 | while(count && *victim == '\0') | 
|  | 223 | eat(1); | 
|  | 224 | if (count && (this_header & 3)) | 
|  | 225 | error("broken padding"); | 
|  | 226 | return 1; | 
|  | 227 | } | 
|  | 228 |  | 
|  | 229 | static int __init maybe_link(void) | 
|  | 230 | { | 
|  | 231 | if (nlink >= 2) { | 
|  | 232 | char *old = find_link(major, minor, ino, collected); | 
|  | 233 | if (old) | 
|  | 234 | return (sys_link(old, collected) < 0) ? -1 : 1; | 
|  | 235 | } | 
|  | 236 | return 0; | 
|  | 237 | } | 
|  | 238 |  | 
|  | 239 | static __initdata int wfd; | 
|  | 240 |  | 
|  | 241 | static int __init do_name(void) | 
|  | 242 | { | 
|  | 243 | state = SkipIt; | 
|  | 244 | next_state = Reset; | 
|  | 245 | if (strcmp(collected, "TRAILER!!!") == 0) { | 
|  | 246 | free_hash(); | 
|  | 247 | return 0; | 
|  | 248 | } | 
|  | 249 | if (dry_run) | 
|  | 250 | return 0; | 
|  | 251 | if (S_ISREG(mode)) { | 
|  | 252 | if (maybe_link() >= 0) { | 
|  | 253 | wfd = sys_open(collected, O_WRONLY|O_CREAT, mode); | 
|  | 254 | if (wfd >= 0) { | 
|  | 255 | sys_fchown(wfd, uid, gid); | 
|  | 256 | sys_fchmod(wfd, mode); | 
|  | 257 | state = CopyFile; | 
|  | 258 | } | 
|  | 259 | } | 
|  | 260 | } else if (S_ISDIR(mode)) { | 
|  | 261 | sys_mkdir(collected, mode); | 
|  | 262 | sys_chown(collected, uid, gid); | 
|  | 263 | sys_chmod(collected, mode); | 
|  | 264 | } else if (S_ISBLK(mode) || S_ISCHR(mode) || | 
|  | 265 | S_ISFIFO(mode) || S_ISSOCK(mode)) { | 
|  | 266 | if (maybe_link() == 0) { | 
|  | 267 | sys_mknod(collected, mode, rdev); | 
|  | 268 | sys_chown(collected, uid, gid); | 
|  | 269 | sys_chmod(collected, mode); | 
|  | 270 | } | 
|  | 271 | } | 
|  | 272 | return 0; | 
|  | 273 | } | 
|  | 274 |  | 
|  | 275 | static int __init do_copy(void) | 
|  | 276 | { | 
|  | 277 | if (count >= body_len) { | 
|  | 278 | sys_write(wfd, victim, body_len); | 
|  | 279 | sys_close(wfd); | 
|  | 280 | eat(body_len); | 
|  | 281 | state = SkipIt; | 
|  | 282 | return 0; | 
|  | 283 | } else { | 
|  | 284 | sys_write(wfd, victim, count); | 
|  | 285 | body_len -= count; | 
|  | 286 | eat(count); | 
|  | 287 | return 1; | 
|  | 288 | } | 
|  | 289 | } | 
|  | 290 |  | 
|  | 291 | static int __init do_symlink(void) | 
|  | 292 | { | 
|  | 293 | collected[N_ALIGN(name_len) + body_len] = '\0'; | 
|  | 294 | sys_symlink(collected + N_ALIGN(name_len), collected); | 
|  | 295 | sys_lchown(collected, uid, gid); | 
|  | 296 | state = SkipIt; | 
|  | 297 | next_state = Reset; | 
|  | 298 | return 0; | 
|  | 299 | } | 
|  | 300 |  | 
|  | 301 | static __initdata int (*actions[])(void) = { | 
|  | 302 | [Start]		= do_start, | 
|  | 303 | [Collect]	= do_collect, | 
|  | 304 | [GotHeader]	= do_header, | 
|  | 305 | [SkipIt]	= do_skip, | 
|  | 306 | [GotName]	= do_name, | 
|  | 307 | [CopyFile]	= do_copy, | 
|  | 308 | [GotSymlink]	= do_symlink, | 
|  | 309 | [Reset]		= do_reset, | 
|  | 310 | }; | 
|  | 311 |  | 
|  | 312 | static int __init write_buffer(char *buf, unsigned len) | 
|  | 313 | { | 
|  | 314 | count = len; | 
|  | 315 | victim = buf; | 
|  | 316 |  | 
|  | 317 | while (!actions[state]()) | 
|  | 318 | ; | 
|  | 319 | return len - count; | 
|  | 320 | } | 
|  | 321 |  | 
|  | 322 | static void __init flush_buffer(char *buf, unsigned len) | 
|  | 323 | { | 
|  | 324 | int written; | 
|  | 325 | if (message) | 
|  | 326 | return; | 
|  | 327 | while ((written = write_buffer(buf, len)) < len && !message) { | 
|  | 328 | char c = buf[written]; | 
|  | 329 | if (c == '0') { | 
|  | 330 | buf += written; | 
|  | 331 | len -= written; | 
|  | 332 | state = Start; | 
|  | 333 | } else if (c == 0) { | 
|  | 334 | buf += written; | 
|  | 335 | len -= written; | 
|  | 336 | state = Reset; | 
|  | 337 | } else | 
|  | 338 | error("junk in compressed archive"); | 
|  | 339 | } | 
|  | 340 | } | 
|  | 341 |  | 
|  | 342 | /* | 
|  | 343 | * gzip declarations | 
|  | 344 | */ | 
|  | 345 |  | 
|  | 346 | #define OF(args)  args | 
|  | 347 |  | 
|  | 348 | #ifndef memzero | 
|  | 349 | #define memzero(s, n)     memset ((s), 0, (n)) | 
|  | 350 | #endif | 
|  | 351 |  | 
|  | 352 | typedef unsigned char  uch; | 
|  | 353 | typedef unsigned short ush; | 
|  | 354 | typedef unsigned long  ulg; | 
|  | 355 |  | 
|  | 356 | #define WSIZE 0x8000    /* window size--must be a power of two, and */ | 
|  | 357 | /*  at least 32K for zip's deflate method */ | 
|  | 358 |  | 
|  | 359 | static uch *inbuf; | 
|  | 360 | static uch *window; | 
|  | 361 |  | 
|  | 362 | static unsigned insize;  /* valid bytes in inbuf */ | 
|  | 363 | static unsigned inptr;   /* index of next byte to be processed in inbuf */ | 
|  | 364 | static unsigned outcnt;  /* bytes in output buffer */ | 
|  | 365 | static long bytes_out; | 
|  | 366 |  | 
|  | 367 | #define get_byte()  (inptr < insize ? inbuf[inptr++] : -1) | 
|  | 368 |  | 
|  | 369 | /* Diagnostic functions (stubbed out) */ | 
|  | 370 | #define Assert(cond,msg) | 
|  | 371 | #define Trace(x) | 
|  | 372 | #define Tracev(x) | 
|  | 373 | #define Tracevv(x) | 
|  | 374 | #define Tracec(c,x) | 
|  | 375 | #define Tracecv(c,x) | 
|  | 376 |  | 
|  | 377 | #define STATIC static | 
|  | 378 | #define INIT __init | 
|  | 379 |  | 
|  | 380 | static void __init flush_window(void); | 
|  | 381 | static void __init error(char *m); | 
|  | 382 | static void __init gzip_mark(void **); | 
|  | 383 | static void __init gzip_release(void **); | 
|  | 384 |  | 
|  | 385 | #include "../lib/inflate.c" | 
|  | 386 |  | 
|  | 387 | static void __init gzip_mark(void **ptr) | 
|  | 388 | { | 
|  | 389 | } | 
|  | 390 |  | 
|  | 391 | static void __init gzip_release(void **ptr) | 
|  | 392 | { | 
|  | 393 | } | 
|  | 394 |  | 
|  | 395 | /* =========================================================================== | 
|  | 396 | * Write the output window window[0..outcnt-1] and update crc and bytes_out. | 
|  | 397 | * (Used for the decompressed data only.) | 
|  | 398 | */ | 
|  | 399 | static void __init flush_window(void) | 
|  | 400 | { | 
|  | 401 | ulg c = crc;         /* temporary variable */ | 
|  | 402 | unsigned n; | 
|  | 403 | uch *in, ch; | 
|  | 404 |  | 
|  | 405 | flush_buffer(window, outcnt); | 
|  | 406 | in = window; | 
|  | 407 | for (n = 0; n < outcnt; n++) { | 
|  | 408 | ch = *in++; | 
|  | 409 | c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); | 
|  | 410 | } | 
|  | 411 | crc = c; | 
|  | 412 | bytes_out += (ulg)outcnt; | 
|  | 413 | outcnt = 0; | 
|  | 414 | } | 
|  | 415 |  | 
|  | 416 | static char * __init unpack_to_rootfs(char *buf, unsigned len, int check_only) | 
|  | 417 | { | 
|  | 418 | int written; | 
|  | 419 | dry_run = check_only; | 
|  | 420 | header_buf = malloc(110); | 
|  | 421 | symlink_buf = malloc(PATH_MAX + N_ALIGN(PATH_MAX) + 1); | 
|  | 422 | name_buf = malloc(N_ALIGN(PATH_MAX)); | 
|  | 423 | window = malloc(WSIZE); | 
|  | 424 | if (!window || !header_buf || !symlink_buf || !name_buf) | 
|  | 425 | panic("can't allocate buffers"); | 
|  | 426 | state = Start; | 
|  | 427 | this_header = 0; | 
|  | 428 | message = NULL; | 
|  | 429 | while (!message && len) { | 
|  | 430 | loff_t saved_offset = this_header; | 
|  | 431 | if (*buf == '0' && !(this_header & 3)) { | 
|  | 432 | state = Start; | 
|  | 433 | written = write_buffer(buf, len); | 
|  | 434 | buf += written; | 
|  | 435 | len -= written; | 
|  | 436 | continue; | 
|  | 437 | } | 
|  | 438 | if (!*buf) { | 
|  | 439 | buf++; | 
|  | 440 | len--; | 
|  | 441 | this_header++; | 
|  | 442 | continue; | 
|  | 443 | } | 
|  | 444 | this_header = 0; | 
|  | 445 | insize = len; | 
|  | 446 | inbuf = buf; | 
|  | 447 | inptr = 0; | 
|  | 448 | outcnt = 0;		/* bytes in output buffer */ | 
|  | 449 | bytes_out = 0; | 
|  | 450 | crc = (ulg)0xffffffffL; /* shift register contents */ | 
|  | 451 | makecrc(); | 
|  | 452 | gunzip(); | 
|  | 453 | if (state != Reset) | 
|  | 454 | error("junk in gzipped archive"); | 
|  | 455 | this_header = saved_offset + inptr; | 
|  | 456 | buf += inptr; | 
|  | 457 | len -= inptr; | 
|  | 458 | } | 
|  | 459 | free(window); | 
|  | 460 | free(name_buf); | 
|  | 461 | free(symlink_buf); | 
|  | 462 | free(header_buf); | 
|  | 463 | return message; | 
|  | 464 | } | 
|  | 465 |  | 
|  | 466 | extern char __initramfs_start[], __initramfs_end[]; | 
|  | 467 | #ifdef CONFIG_BLK_DEV_INITRD | 
|  | 468 | #include <linux/initrd.h> | 
| Haren Myneni | 9c15e85 | 2006-02-10 01:51:05 -0800 | [diff] [blame] | 469 | #include <linux/kexec.h> | 
| Jan Beulich | 0f3d2bd | 2005-09-13 01:25:12 -0700 | [diff] [blame] | 470 |  | 
|  | 471 | static void __init free_initrd(void) | 
|  | 472 | { | 
| Haren Myneni | 9c15e85 | 2006-02-10 01:51:05 -0800 | [diff] [blame] | 473 | #ifdef CONFIG_KEXEC | 
|  | 474 | unsigned long crashk_start = (unsigned long)__va(crashk_res.start); | 
|  | 475 | unsigned long crashk_end   = (unsigned long)__va(crashk_res.end); | 
|  | 476 |  | 
|  | 477 | /* | 
|  | 478 | * If the initrd region is overlapped with crashkernel reserved region, | 
|  | 479 | * free only memory that is not part of crashkernel region. | 
|  | 480 | */ | 
|  | 481 | if (initrd_start < crashk_end && initrd_end > crashk_start) { | 
|  | 482 | /* | 
|  | 483 | * Initialize initrd memory region since the kexec boot does | 
|  | 484 | * not do. | 
|  | 485 | */ | 
|  | 486 | memset((void *)initrd_start, 0, initrd_end - initrd_start); | 
|  | 487 | if (initrd_start < crashk_start) | 
|  | 488 | free_initrd_mem(initrd_start, crashk_start); | 
|  | 489 | if (initrd_end > crashk_end) | 
|  | 490 | free_initrd_mem(crashk_end, initrd_end); | 
|  | 491 | } else | 
|  | 492 | #endif | 
|  | 493 | free_initrd_mem(initrd_start, initrd_end); | 
|  | 494 |  | 
| Jan Beulich | 0f3d2bd | 2005-09-13 01:25:12 -0700 | [diff] [blame] | 495 | initrd_start = 0; | 
|  | 496 | initrd_end = 0; | 
|  | 497 | } | 
|  | 498 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 499 | #endif | 
|  | 500 |  | 
|  | 501 | void __init populate_rootfs(void) | 
|  | 502 | { | 
|  | 503 | char *err = unpack_to_rootfs(__initramfs_start, | 
|  | 504 | __initramfs_end - __initramfs_start, 0); | 
|  | 505 | if (err) | 
|  | 506 | panic(err); | 
|  | 507 | #ifdef CONFIG_BLK_DEV_INITRD | 
|  | 508 | if (initrd_start) { | 
|  | 509 | int fd; | 
|  | 510 | printk(KERN_INFO "checking if image is initramfs..."); | 
|  | 511 | err = unpack_to_rootfs((char *)initrd_start, | 
|  | 512 | initrd_end - initrd_start, 1); | 
|  | 513 | if (!err) { | 
|  | 514 | printk(" it is\n"); | 
|  | 515 | unpack_to_rootfs((char *)initrd_start, | 
|  | 516 | initrd_end - initrd_start, 0); | 
| Jan Beulich | 0f3d2bd | 2005-09-13 01:25:12 -0700 | [diff] [blame] | 517 | free_initrd(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 518 | return; | 
|  | 519 | } | 
|  | 520 | printk("it isn't (%s); looks like an initrd\n", err); | 
|  | 521 | fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 700); | 
|  | 522 | if (fd >= 0) { | 
|  | 523 | sys_write(fd, (char *)initrd_start, | 
|  | 524 | initrd_end - initrd_start); | 
|  | 525 | sys_close(fd); | 
| Jan Beulich | 0f3d2bd | 2005-09-13 01:25:12 -0700 | [diff] [blame] | 526 | free_initrd(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 527 | } | 
|  | 528 | } | 
|  | 529 | #endif | 
|  | 530 | } |