读取系统调用:当输入的字节多于计数参数时,多余的字节溢出到 shell 并作为下一个命令执行
read system call: When input has more bytes than count argument, excess bytes overflow to shell and get executed as next command
我正在编写代码以使用I/O 系统调用 实现tee
命令。这是 Michael Kerrisk The Linux Programming Interface 一书中的练习。
我的系统是Ubuntu16.04.
我对 linux 编程没有经验。
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "tlpi_hdr.h"
#define MAX_READ 20
int
main(int argc, char *argv[])
{
int fileFd;
ssize_t numRead;
char buffer[MAX_READ + 10];
// command example: tee_practice tfile
if (argc != 2 || strcmp(argv[1], "--help") == 0)
usageErr("Usage error\nDid you supply the filename?\n");
fileFd = open(argv[1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (fileFd == -1)
errExit("open");
if ((numRead = read(STDIN_FILENO, buffer, MAX_READ)) == -1) // HERE bytes overflow to shell and get executed as next command
errExit("read");
buffer[numRead] = '[=11=]';
if (write(STDOUT_FILENO, buffer, MAX_READ) == -1)
errExit("write");
if (write(fileFd, buffer, MAX_READ) == -1)
errExit("write");
exit(EXIT_SUCCESS);
}
输入时
Hi I am writing a few lines
程序将前 20 个字节(您好,我正在写一个 fe)写入文件和标准输出。剩余的字节 (w lines
) 作为下一个 shell 命令执行,我想防止这种情况发生。
我哪里错了?
编辑
不完全是最小的可重现示例,但它可以工作并且包括 errExit() 和 usageErro()。
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdarg.h>
#include <sys/types.h> /* Type definitions used by many programs */
#include <stdio.h> /* Standard I/O functions */
#include <stdlib.h> /* Prototypes of commonly used library functions,
plus EXIT_SUCCESS and EXIT_FAILURE constants */
#include <unistd.h> /* Prototypes for many system calls */
#include <errno.h> /* Declares errno and defines error constants */
#include <string.h> /* Commonly used string-handling functions */
#define MAX_READ 20
#ifndef ERROR_FUNCTIONS_H
#define ERROR_FUNCTIONS_H
#ifdef __GNUC__
/* This macro stops 'gcc -Wall' complaining that "control reaches
end of non-void function" if we use the following functions to
terminate main() or some other non-void function. */
#define NORETURN __attribute__ ((__noreturn__))
#else
#define NORETURN
#endif
#endif
void errMsg(const char *format, ...);
void errExit(const char *format, ...) NORETURN ;
void usageErr(const char *format, ...) NORETURN ;
typedef enum { FALSE, TRUE } Boolean;
static char *ename[] = {
/* 0 */ "",
/* 1 */ "EPERM", "ENOENT", "ESRCH", "EINTR", "EIO", "ENXIO",
/* 7 */ "E2BIG", "ENOEXEC", "EBADF", "ECHILD",
/* 11 */ "EAGAIN/EWOULDBLOCK", "ENOMEM", "EACCES", "EFAULT",
/* 15 */ "ENOTBLK", "EBUSY", "EEXIST", "EXDEV", "ENODEV",
/* 20 */ "ENOTDIR", "EISDIR", "EINVAL", "ENFILE", "EMFILE",
/* 25 */ "ENOTTY", "ETXTBSY", "EFBIG", "ENOSPC", "ESPIPE",
/* 30 */ "EROFS", "EMLINK", "EPIPE", "EDOM", "ERANGE",
/* 35 */ "EDEADLK/EDEADLOCK", "ENAMETOOLONG", "ENOLCK", "ENOSYS",
/* 39 */ "ENOTEMPTY", "ELOOP", "", "ENOMSG", "EIDRM", "ECHRNG",
/* 45 */ "EL2NSYNC", "EL3HLT", "EL3RST", "ELNRNG", "EUNATCH",
/* 50 */ "ENOCSI", "EL2HLT", "EBADE", "EBADR", "EXFULL", "ENOANO",
/* 56 */ "EBADRQC", "EBADSLT", "", "EBFONT", "ENOSTR", "ENODATA",
/* 62 */ "ETIME", "ENOSR", "ENONET", "ENOPKG", "EREMOTE",
/* 67 */ "ENOLINK", "EADV", "ESRMNT", "ECOMM", "EPROTO",
/* 72 */ "EMULTIHOP", "EDOTDOT", "EBADMSG", "EOVERFLOW",
/* 76 */ "ENOTUNIQ", "EBADFD", "EREMCHG", "ELIBACC", "ELIBBAD",
/* 81 */ "ELIBSCN", "ELIBMAX", "ELIBEXEC", "EILSEQ", "ERESTART",
/* 86 */ "ESTRPIPE", "EUSERS", "ENOTSOCK", "EDESTADDRREQ",
/* 90 */ "EMSGSIZE", "EPROTOTYPE", "ENOPROTOOPT",
/* 93 */ "EPROTONOSUPPORT", "ESOCKTNOSUPPORT",
/* 95 */ "EOPNOTSUPP/ENOTSUP", "EPFNOSUPPORT", "EAFNOSUPPORT",
/* 98 */ "EADDRINUSE", "EADDRNOTAVAIL", "ENETDOWN", "ENETUNREACH",
/* 102 */ "ENETRESET", "ECONNABORTED", "ECONNRESET", "ENOBUFS",
/* 106 */ "EISCONN", "ENOTCONN", "ESHUTDOWN", "ETOOMANYREFS",
/* 110 */ "ETIMEDOUT", "ECONNREFUSED", "EHOSTDOWN", "EHOSTUNREACH",
/* 114 */ "EALREADY", "EINPROGRESS", "ESTALE", "EUCLEAN",
/* 118 */ "ENOTNAM", "ENAVAIL", "EISNAM", "EREMOTEIO", "EDQUOT",
/* 123 */ "ENOMEDIUM", "EMEDIUMTYPE", "ECANCELED", "ENOKEY",
/* 127 */ "EKEYEXPIRED", "EKEYREVOKED", "EKEYREJECTED",
/* 130 */ "EOWNERDEAD", "ENOTRECOVERABLE", "ERFKILL", "EHWPOISON"
};
#define MAX_ENAME 133
#ifdef __GNUC__
__attribute__ ((__noreturn__))
#endif
static void
terminate(Boolean useExit3)
{
char *s;
/* Dump core if EF_DUMPCORE environment variable is defined and
is a nonempty string; otherwise call exit(3) or _exit(2),
depending on the value of 'useExit3'. */
s = getenv("EF_DUMPCORE");
if (s != NULL && *s != '[=12=]')
abort();
else if (useExit3)
exit(EXIT_FAILURE);
else
_exit(EXIT_FAILURE);
}
static void
outputError(Boolean useErr, int err, Boolean flushStdout,
const char *format, va_list ap)
{
#define BUF_SIZE 500
char buf[BUF_SIZE], userMsg[BUF_SIZE], errText[BUF_SIZE];
vsnprintf(userMsg, BUF_SIZE, format, ap);
if (useErr)
snprintf(errText, BUF_SIZE, " [%s %s]",
(err > 0 && err <= MAX_ENAME) ?
ename[err] : "?UNKNOWN?", strerror(err));
else
snprintf(errText, BUF_SIZE, ":");
snprintf(buf, BUF_SIZE, "ERROR%s %s\n", errText, userMsg);
if (flushStdout)
fflush(stdout); /* Flush any pending stdout */
fputs(buf, stderr);
fflush(stderr); /* In case stderr is not line-buffered */
}
void
usageErr(const char *format, ...)
{
va_list argList;
fflush(stdout); /* Flush any pending stdout */
fprintf(stderr, "Usage: ");
va_start(argList, format);
vfprintf(stderr, format, argList);
va_end(argList);
fflush(stderr); /* In case stderr is not line-buffered */
exit(EXIT_FAILURE);
}
void
errExit(const char *format, ...)
{
va_list argList;
va_start(argList, format);
outputError(TRUE, errno, TRUE, format, argList);
va_end(argList);
terminate(TRUE);
}
int
main(int argc, char *argv[])
{
int fileFd;
ssize_t numRead;
char buffer[MAX_READ + 10];
if (argc != 2 || strcmp(argv[1], "--help") == 0)
usageErr("Usage error\nDid you supply the filename?\n");
fileFd = open(argv[1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (fileFd == -1)
errExit("open");
if ((numRead = read(STDIN_FILENO, buffer, MAX_READ)) == -1)
errExit("read");
buffer[numRead] = '[=12=]';
if (write(STDOUT_FILENO, buffer, MAX_READ) == -1)
errExit("write");
if (write(fileFd, buffer, MAX_READ) == -1)
errExit("write");
exit(EXIT_SUCCESS);
}
你的程序只读了20个字节。在这 20 个字节之后,无论程序的标准输入连接到什么,无论要读取什么,都留在那里。可以是来自终端、管道缓冲区或文件的输入。
如果您使用 stdio 输入函数(fgets()
、fread()
等),
他们会向 OS 请求更大的数据块(通常是 4096 B,glibc 在 Linux 上),所以问题不会出现这么短的输入。
要获得所有内容,您需要循环读取所有内容直到 EOF,并且由于您正在实施 tee
,还将所有内容复制到标准输出和输出文件。
即这个方向的东西:
#include<unistd.h>
#include<stdio.h>
int main(void)
{
/* setup ... */
char buf[1024];
while(1) {
int n = read(fd, buf, 1024);
if (n == 0)
break; /* EOF */
if (n == -1) {
perror("read");
return 1;
}
write(STDOUT_FILENO, buf, n);
write(outfd, buf, n);
}
return 0;
}
但也要检查 write()
调用中的错误。此外,从技术上讲,write()
可能 return 没有写出你要求的所有内容,即 write(outfd, buf, n)
可能写的少于你要求的 n
字节。但是这种情况很少见。
我正在编写代码以使用I/O 系统调用 实现tee
命令。这是 Michael Kerrisk The Linux Programming Interface 一书中的练习。
我的系统是Ubuntu16.04.
我对 linux 编程没有经验。
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "tlpi_hdr.h"
#define MAX_READ 20
int
main(int argc, char *argv[])
{
int fileFd;
ssize_t numRead;
char buffer[MAX_READ + 10];
// command example: tee_practice tfile
if (argc != 2 || strcmp(argv[1], "--help") == 0)
usageErr("Usage error\nDid you supply the filename?\n");
fileFd = open(argv[1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (fileFd == -1)
errExit("open");
if ((numRead = read(STDIN_FILENO, buffer, MAX_READ)) == -1) // HERE bytes overflow to shell and get executed as next command
errExit("read");
buffer[numRead] = '[=11=]';
if (write(STDOUT_FILENO, buffer, MAX_READ) == -1)
errExit("write");
if (write(fileFd, buffer, MAX_READ) == -1)
errExit("write");
exit(EXIT_SUCCESS);
}
输入时
Hi I am writing a few lines
程序将前 20 个字节(您好,我正在写一个 fe)写入文件和标准输出。剩余的字节 (w lines
) 作为下一个 shell 命令执行,我想防止这种情况发生。
我哪里错了?
编辑
不完全是最小的可重现示例,但它可以工作并且包括 errExit() 和 usageErro()。
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdarg.h>
#include <sys/types.h> /* Type definitions used by many programs */
#include <stdio.h> /* Standard I/O functions */
#include <stdlib.h> /* Prototypes of commonly used library functions,
plus EXIT_SUCCESS and EXIT_FAILURE constants */
#include <unistd.h> /* Prototypes for many system calls */
#include <errno.h> /* Declares errno and defines error constants */
#include <string.h> /* Commonly used string-handling functions */
#define MAX_READ 20
#ifndef ERROR_FUNCTIONS_H
#define ERROR_FUNCTIONS_H
#ifdef __GNUC__
/* This macro stops 'gcc -Wall' complaining that "control reaches
end of non-void function" if we use the following functions to
terminate main() or some other non-void function. */
#define NORETURN __attribute__ ((__noreturn__))
#else
#define NORETURN
#endif
#endif
void errMsg(const char *format, ...);
void errExit(const char *format, ...) NORETURN ;
void usageErr(const char *format, ...) NORETURN ;
typedef enum { FALSE, TRUE } Boolean;
static char *ename[] = {
/* 0 */ "",
/* 1 */ "EPERM", "ENOENT", "ESRCH", "EINTR", "EIO", "ENXIO",
/* 7 */ "E2BIG", "ENOEXEC", "EBADF", "ECHILD",
/* 11 */ "EAGAIN/EWOULDBLOCK", "ENOMEM", "EACCES", "EFAULT",
/* 15 */ "ENOTBLK", "EBUSY", "EEXIST", "EXDEV", "ENODEV",
/* 20 */ "ENOTDIR", "EISDIR", "EINVAL", "ENFILE", "EMFILE",
/* 25 */ "ENOTTY", "ETXTBSY", "EFBIG", "ENOSPC", "ESPIPE",
/* 30 */ "EROFS", "EMLINK", "EPIPE", "EDOM", "ERANGE",
/* 35 */ "EDEADLK/EDEADLOCK", "ENAMETOOLONG", "ENOLCK", "ENOSYS",
/* 39 */ "ENOTEMPTY", "ELOOP", "", "ENOMSG", "EIDRM", "ECHRNG",
/* 45 */ "EL2NSYNC", "EL3HLT", "EL3RST", "ELNRNG", "EUNATCH",
/* 50 */ "ENOCSI", "EL2HLT", "EBADE", "EBADR", "EXFULL", "ENOANO",
/* 56 */ "EBADRQC", "EBADSLT", "", "EBFONT", "ENOSTR", "ENODATA",
/* 62 */ "ETIME", "ENOSR", "ENONET", "ENOPKG", "EREMOTE",
/* 67 */ "ENOLINK", "EADV", "ESRMNT", "ECOMM", "EPROTO",
/* 72 */ "EMULTIHOP", "EDOTDOT", "EBADMSG", "EOVERFLOW",
/* 76 */ "ENOTUNIQ", "EBADFD", "EREMCHG", "ELIBACC", "ELIBBAD",
/* 81 */ "ELIBSCN", "ELIBMAX", "ELIBEXEC", "EILSEQ", "ERESTART",
/* 86 */ "ESTRPIPE", "EUSERS", "ENOTSOCK", "EDESTADDRREQ",
/* 90 */ "EMSGSIZE", "EPROTOTYPE", "ENOPROTOOPT",
/* 93 */ "EPROTONOSUPPORT", "ESOCKTNOSUPPORT",
/* 95 */ "EOPNOTSUPP/ENOTSUP", "EPFNOSUPPORT", "EAFNOSUPPORT",
/* 98 */ "EADDRINUSE", "EADDRNOTAVAIL", "ENETDOWN", "ENETUNREACH",
/* 102 */ "ENETRESET", "ECONNABORTED", "ECONNRESET", "ENOBUFS",
/* 106 */ "EISCONN", "ENOTCONN", "ESHUTDOWN", "ETOOMANYREFS",
/* 110 */ "ETIMEDOUT", "ECONNREFUSED", "EHOSTDOWN", "EHOSTUNREACH",
/* 114 */ "EALREADY", "EINPROGRESS", "ESTALE", "EUCLEAN",
/* 118 */ "ENOTNAM", "ENAVAIL", "EISNAM", "EREMOTEIO", "EDQUOT",
/* 123 */ "ENOMEDIUM", "EMEDIUMTYPE", "ECANCELED", "ENOKEY",
/* 127 */ "EKEYEXPIRED", "EKEYREVOKED", "EKEYREJECTED",
/* 130 */ "EOWNERDEAD", "ENOTRECOVERABLE", "ERFKILL", "EHWPOISON"
};
#define MAX_ENAME 133
#ifdef __GNUC__
__attribute__ ((__noreturn__))
#endif
static void
terminate(Boolean useExit3)
{
char *s;
/* Dump core if EF_DUMPCORE environment variable is defined and
is a nonempty string; otherwise call exit(3) or _exit(2),
depending on the value of 'useExit3'. */
s = getenv("EF_DUMPCORE");
if (s != NULL && *s != '[=12=]')
abort();
else if (useExit3)
exit(EXIT_FAILURE);
else
_exit(EXIT_FAILURE);
}
static void
outputError(Boolean useErr, int err, Boolean flushStdout,
const char *format, va_list ap)
{
#define BUF_SIZE 500
char buf[BUF_SIZE], userMsg[BUF_SIZE], errText[BUF_SIZE];
vsnprintf(userMsg, BUF_SIZE, format, ap);
if (useErr)
snprintf(errText, BUF_SIZE, " [%s %s]",
(err > 0 && err <= MAX_ENAME) ?
ename[err] : "?UNKNOWN?", strerror(err));
else
snprintf(errText, BUF_SIZE, ":");
snprintf(buf, BUF_SIZE, "ERROR%s %s\n", errText, userMsg);
if (flushStdout)
fflush(stdout); /* Flush any pending stdout */
fputs(buf, stderr);
fflush(stderr); /* In case stderr is not line-buffered */
}
void
usageErr(const char *format, ...)
{
va_list argList;
fflush(stdout); /* Flush any pending stdout */
fprintf(stderr, "Usage: ");
va_start(argList, format);
vfprintf(stderr, format, argList);
va_end(argList);
fflush(stderr); /* In case stderr is not line-buffered */
exit(EXIT_FAILURE);
}
void
errExit(const char *format, ...)
{
va_list argList;
va_start(argList, format);
outputError(TRUE, errno, TRUE, format, argList);
va_end(argList);
terminate(TRUE);
}
int
main(int argc, char *argv[])
{
int fileFd;
ssize_t numRead;
char buffer[MAX_READ + 10];
if (argc != 2 || strcmp(argv[1], "--help") == 0)
usageErr("Usage error\nDid you supply the filename?\n");
fileFd = open(argv[1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (fileFd == -1)
errExit("open");
if ((numRead = read(STDIN_FILENO, buffer, MAX_READ)) == -1)
errExit("read");
buffer[numRead] = '[=12=]';
if (write(STDOUT_FILENO, buffer, MAX_READ) == -1)
errExit("write");
if (write(fileFd, buffer, MAX_READ) == -1)
errExit("write");
exit(EXIT_SUCCESS);
}
你的程序只读了20个字节。在这 20 个字节之后,无论程序的标准输入连接到什么,无论要读取什么,都留在那里。可以是来自终端、管道缓冲区或文件的输入。
如果您使用 stdio 输入函数(fgets()
、fread()
等),
他们会向 OS 请求更大的数据块(通常是 4096 B,glibc 在 Linux 上),所以问题不会出现这么短的输入。
要获得所有内容,您需要循环读取所有内容直到 EOF,并且由于您正在实施 tee
,还将所有内容复制到标准输出和输出文件。
即这个方向的东西:
#include<unistd.h>
#include<stdio.h>
int main(void)
{
/* setup ... */
char buf[1024];
while(1) {
int n = read(fd, buf, 1024);
if (n == 0)
break; /* EOF */
if (n == -1) {
perror("read");
return 1;
}
write(STDOUT_FILENO, buf, n);
write(outfd, buf, n);
}
return 0;
}
但也要检查 write()
调用中的错误。此外,从技术上讲,write()
可能 return 没有写出你要求的所有内容,即 write(outfd, buf, n)
可能写的少于你要求的 n
字节。但是这种情况很少见。