chan_user.c revision 89df6bfc04059716de2eb2fe529f05b3e124fafd
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Licensed under the GPL 41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <unistd.h> 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <stdlib.h> 81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <errno.h> 91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <termios.h> 101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <string.h> 111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <signal.h> 121d2ddcfb1935c9c0e98c4295458b01f24e3274f9Jeff Dike#include <sched.h> 131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <sys/stat.h> 141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <sys/ioctl.h> 151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <sys/socket.h> 161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "kern_util.h" 171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "chan_user.h" 181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "user.h" 191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "os.h" 201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "choose-mode.h" 211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "mode.h" 221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2355c033c1f6cdedc350c79c3198b542e3ab496899Paolo 'Blaisorblade' Giarrussoint generic_console_write(int fd, const char *buf, int n) 241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct termios save, new; 261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int err; 271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if(isatty(fd)){ 291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds CATCH_EINTR(err = tcgetattr(fd, &save)); 301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (err) 311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto error; 321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds new = save; 331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* The terminal becomes a bit less raw, to handle \n also as 341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * "Carriage Return", not only as "New Line". Otherwise, the new 351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * line won't start at the first column.*/ 361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds new.c_oflag |= OPOST; 371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds CATCH_EINTR(err = tcsetattr(fd, TCSAFLUSH, &new)); 381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (err) 391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto error; 401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err = generic_write(fd, buf, n, NULL); 421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Restore raw mode, in any case; we *must* ignore any error apart 431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * EINTR, except for debug.*/ 441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if(isatty(fd)) 451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds CATCH_EINTR(tcsetattr(fd, TCSAFLUSH, &save)); 461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return(err); 471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserror: 481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return(-errno); 491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * UML SIGWINCH handling 531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The point of this is to handle SIGWINCH on consoles which have host ttys and 551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * relay them inside UML to whatever might be running on the console and cares 561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * about the window size (since SIGWINCH notifies about terminal size changes). 571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * So, we have a separate thread for each host tty attached to a UML device 591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (side-issue - I'm annoyed that one thread can't have multiple controlling 601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ttys for purposed of handling SIGWINCH, but I imagine there are other reasons 611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * that doesn't make any sense). 621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * SIGWINCH can't be received synchronously, so you have to set up to receive it 641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * as a signal. That being the case, if you are going to wait for it, it is 65ed1b58d8b53519e10a35c6a2bb49cac35f439621Bodo Stroesser * convenient to sit in sigsuspend() and wait for the signal to bounce you out of 661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it (see below for how we make sure to exit only on SIGWINCH). 671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void winch_handler(int sig) 701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct winch_data { 741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int pty_fd; 751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int pipe_fd; 761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int winch_thread(void *arg) 791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct winch_data *data = arg; 811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sigset_t sigs; 821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int pty_fd, pipe_fd; 831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int count, err; 841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds char c = 1; 851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pty_fd = data->pty_fd; 871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pipe_fd = data->pipe_fd; 88a6ea4cceed18edebe1eb6001cb9e0f88cd741a6cJeff Dike count = os_write_file(pipe_fd, &c, sizeof(c)); 891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if(count != sizeof(c)) 901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("winch_thread : failed to write synchronization " 911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds "byte, err = %d\n", -count); 921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* We are not using SIG_IGN on purpose, so don't fix it as I thought to 94ed1b58d8b53519e10a35c6a2bb49cac35f439621Bodo Stroesser * do! If using SIG_IGN, the sigsuspend() call below would not stop on 951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * SIGWINCH. */ 961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds signal(SIGWINCH, winch_handler); 981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sigfillset(&sigs); 99ed1b58d8b53519e10a35c6a2bb49cac35f439621Bodo Stroesser /* Block all signals possible. */ 1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if(sigprocmask(SIG_SETMASK, &sigs, NULL) < 0){ 1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("winch_thread : sigprocmask failed, errno = %d\n", 1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds errno); 1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds exit(1); 1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 105ed1b58d8b53519e10a35c6a2bb49cac35f439621Bodo Stroesser /* In sigsuspend(), block anything else than SIGWINCH. */ 106ed1b58d8b53519e10a35c6a2bb49cac35f439621Bodo Stroesser sigdelset(&sigs, SIGWINCH); 1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if(setsid() < 0){ 1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("winch_thread : setsid failed, errno = %d\n", errno); 1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds exit(1); 1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err = os_new_tty_pgrp(pty_fd, os_getpid()); 1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if(err < 0){ 1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("winch_thread : new_tty_pgrp failed, err = %d\n", -err); 1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds exit(1); 1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* These are synchronization calls between various UML threads on the 1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * host - since they are not different kernel threads, we cannot use 1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * kernel semaphores. We don't use SysV semaphores because they are 12203a67a46af8647b2c7825107045ecae641e103d3Jan Engelhardt * persistent. */ 123a6ea4cceed18edebe1eb6001cb9e0f88cd741a6cJeff Dike count = os_read_file(pipe_fd, &c, sizeof(c)); 1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if(count != sizeof(c)) 1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("winch_thread : failed to read synchronization byte, " 1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds "err = %d\n", -count); 1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while(1){ 1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* This will be interrupted by SIGWINCH only, since other signals 1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * are blocked.*/ 131ed1b58d8b53519e10a35c6a2bb49cac35f439621Bodo Stroesser sigsuspend(&sigs); 1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 133a6ea4cceed18edebe1eb6001cb9e0f88cd741a6cJeff Dike count = os_write_file(pipe_fd, &c, sizeof(c)); 1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if(count != sizeof(c)) 1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("winch_thread : write failed, err = %d\n", 1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds -count); 1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int winch_tramp(int fd, struct tty_struct *tty, int *fd_out) 1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct winch_data data; 1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long stack; 1441f96ddb4fb40961a8ebebf7a00bbfaad55aacbd2Jeff Dike int fds[2], n, err; 1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds char c; 1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err = os_pipe(fds, 1, 1); 1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if(err < 0){ 1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("winch_tramp : os_pipe failed, err = %d\n", -err); 1501f96ddb4fb40961a8ebebf7a00bbfaad55aacbd2Jeff Dike goto out; 1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data = ((struct winch_data) { .pty_fd = fd, 1541d2ddcfb1935c9c0e98c4295458b01f24e3274f9Jeff Dike .pipe_fd = fds[1] } ); 1551d2ddcfb1935c9c0e98c4295458b01f24e3274f9Jeff Dike /* CLONE_FILES so this thread doesn't hold open files which are open 1561d2ddcfb1935c9c0e98c4295458b01f24e3274f9Jeff Dike * now, but later closed. This is a problem with /dev/net/tun. 1571d2ddcfb1935c9c0e98c4295458b01f24e3274f9Jeff Dike */ 1581d2ddcfb1935c9c0e98c4295458b01f24e3274f9Jeff Dike err = run_helper_thread(winch_thread, &data, CLONE_FILES, &stack, 0); 1591f96ddb4fb40961a8ebebf7a00bbfaad55aacbd2Jeff Dike if(err < 0){ 160b16895b63c504698b0c3ab26ca3c41a4fa162a42Andrew Morton printk("fork of winch_thread failed - errno = %d\n", -err); 1611f96ddb4fb40961a8ebebf7a00bbfaad55aacbd2Jeff Dike goto out_close; 1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *fd_out = fds[0]; 165a6ea4cceed18edebe1eb6001cb9e0f88cd741a6cJeff Dike n = os_read_file(fds[0], &c, sizeof(c)); 1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if(n != sizeof(c)){ 1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("winch_tramp : failed to read synchronization byte\n"); 1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("read failed, err = %d\n", -n); 1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("fd %d will not support SIGWINCH\n", fd); 1701f96ddb4fb40961a8ebebf7a00bbfaad55aacbd2Jeff Dike err = -EINVAL; 1711d2ddcfb1935c9c0e98c4295458b01f24e3274f9Jeff Dike goto out_close; 1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 17389df6bfc04059716de2eb2fe529f05b3e124fafdEduard-Gabriel Munteanu 17489df6bfc04059716de2eb2fe529f05b3e124fafdEduard-Gabriel Munteanu if (os_set_fd_block(*fd_out, 0)) { 17589df6bfc04059716de2eb2fe529f05b3e124fafdEduard-Gabriel Munteanu printk("winch_tramp: failed to set thread_fd non-blocking.\n"); 17689df6bfc04059716de2eb2fe529f05b3e124fafdEduard-Gabriel Munteanu goto out_close; 17789df6bfc04059716de2eb2fe529f05b3e124fafdEduard-Gabriel Munteanu } 17889df6bfc04059716de2eb2fe529f05b3e124fafdEduard-Gabriel Munteanu 17989df6bfc04059716de2eb2fe529f05b3e124fafdEduard-Gabriel Munteanu return err; 1801f96ddb4fb40961a8ebebf7a00bbfaad55aacbd2Jeff Dike 1811f96ddb4fb40961a8ebebf7a00bbfaad55aacbd2Jeff Dike out_close: 1821f96ddb4fb40961a8ebebf7a00bbfaad55aacbd2Jeff Dike os_close_file(fds[1]); 1831f96ddb4fb40961a8ebebf7a00bbfaad55aacbd2Jeff Dike os_close_file(fds[0]); 1841f96ddb4fb40961a8ebebf7a00bbfaad55aacbd2Jeff Dike out: 1851f96ddb4fb40961a8ebebf7a00bbfaad55aacbd2Jeff Dike return err; 1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid register_winch(int fd, struct tty_struct *tty) 1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1901f96ddb4fb40961a8ebebf7a00bbfaad55aacbd2Jeff Dike int pid, thread, thread_fd = -1; 1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int count; 1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds char c = 1; 1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if(!isatty(fd)) 1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pid = tcgetpgrp(fd); 1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if(!CHOOSE_MODE_PROC(is_tracer_winch, is_skas_winch, pid, fd, 1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tty) && (pid == -1)){ 2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds thread = winch_tramp(fd, tty, &thread_fd); 201da00d9a5466558ccd9e7b7d04b13d7cb9160c876Jeff Dike if(thread > 0){ 2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds register_winch_irq(thread_fd, fd, thread, tty); 2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 204a6ea4cceed18edebe1eb6001cb9e0f88cd741a6cJeff Dike count = os_write_file(thread_fd, &c, sizeof(c)); 2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if(count != sizeof(c)) 2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("register_winch : failed to write " 2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds "synchronization byte, err = %d\n", 2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds -count); 2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 212