qrexec-lib: convert tabs to spaces

- Fix compile error on gcc 6 (-Werror=misleading-indentation)
- Follow coding style: https://www.qubes-os.org/doc/coding-style/
This commit is contained in:
Marek Marczykowski-Górecki 2016-06-02 02:32:46 +02:00
parent 93f676d998
commit 410ad3d25f
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724
11 changed files with 648 additions and 648 deletions

View File

@ -28,88 +28,88 @@
static int total_mem; static int total_mem;
static char *limited_malloc(int len) static char *limited_malloc(int len)
{ {
char *ret; char *ret;
total_mem += len; total_mem += len;
if (total_mem > BUFFER_LIMIT) { if (total_mem > BUFFER_LIMIT) {
fprintf(stderr, "attempt to allocate >BUFFER_LIMIT\n"); fprintf(stderr, "attempt to allocate >BUFFER_LIMIT\n");
exit(1); exit(1);
} }
ret = malloc(len); ret = malloc(len);
if (!ret) { if (!ret) {
perror("malloc"); perror("malloc");
exit(1); exit(1);
} }
return ret; return ret;
} }
static void limited_free(char *ptr, int len) static void limited_free(char *ptr, int len)
{ {
free(ptr); free(ptr);
total_mem -= len; total_mem -= len;
} }
void buffer_init(struct buffer *b) void buffer_init(struct buffer *b)
{ {
b->buflen = 0; b->buflen = 0;
b->data = NULL; b->data = NULL;
} }
void buffer_free(struct buffer *b) void buffer_free(struct buffer *b)
{ {
if (b->buflen) if (b->buflen)
limited_free(b->data, b->buflen); limited_free(b->data, b->buflen);
buffer_init(b); buffer_init(b);
} }
/* /*
The following two functions can be made much more efficient. The following two functions can be made much more efficient.
Yet the profiling output show they are not significant CPU hogs, so Yet the profiling output show they are not significant CPU hogs, so
we keep them so simple to make them obviously correct. we keep them so simple to make them obviously correct.
*/ */
void buffer_append(struct buffer *b, const char *data, int len) void buffer_append(struct buffer *b, const char *data, int len)
{ {
int newsize; int newsize;
char *qdata; char *qdata;
if (len < 0 || len > BUFFER_LIMIT) { if (len < 0 || len > BUFFER_LIMIT) {
fprintf(stderr, "buffer_append %d\n", len); fprintf(stderr, "buffer_append %d\n", len);
exit(1); exit(1);
} }
if (len == 0) if (len == 0)
return; return;
newsize = len + b->buflen; newsize = len + b->buflen;
qdata = limited_malloc(len + b->buflen); qdata = limited_malloc(len + b->buflen);
memcpy(qdata, b->data, b->buflen); memcpy(qdata, b->data, b->buflen);
memcpy(qdata + b->buflen, data, len); memcpy(qdata + b->buflen, data, len);
buffer_free(b); buffer_free(b);
b->buflen = newsize; b->buflen = newsize;
b->data = qdata; b->data = qdata;
} }
void buffer_remove(struct buffer *b, int len) void buffer_remove(struct buffer *b, int len)
{ {
int newsize; int newsize;
char *qdata = NULL; char *qdata = NULL;
if (len < 0 || len > b->buflen) { if (len < 0 || len > b->buflen) {
fprintf(stderr, "buffer_remove %d/%d\n", len, b->buflen); fprintf(stderr, "buffer_remove %d/%d\n", len, b->buflen);
exit(1); exit(1);
} }
newsize = b->buflen - len; newsize = b->buflen - len;
if (newsize > 0) { if (newsize > 0) {
qdata = limited_malloc(newsize); qdata = limited_malloc(newsize);
memcpy(qdata, b->data + len, newsize); memcpy(qdata, b->data + len, newsize);
} }
buffer_free(b); buffer_free(b);
b->buflen = newsize; b->buflen = newsize;
b->data = qdata; b->data = qdata;
} }
int buffer_len(struct buffer *b) int buffer_len(struct buffer *b)
{ {
return b->buflen; return b->buflen;
} }
void *buffer_data(struct buffer *b) void *buffer_data(struct buffer *b)
{ {
return b->data; return b->data;
} }

View File

@ -6,44 +6,44 @@
notify_progress_t *notify_progress_func = NULL; notify_progress_t *notify_progress_func = NULL;
void register_notify_progress(notify_progress_t *func) void register_notify_progress(notify_progress_t *func)
{ {
notify_progress_func = func; notify_progress_func = func;
} }
int copy_file(int outfd, int infd, long long size, unsigned long *crc32) int copy_file(int outfd, int infd, long long size, unsigned long *crc32)
{ {
char buf[4096]; char buf[4096];
long long written = 0; long long written = 0;
int ret; int ret;
int count; int count;
while (written < size) { while (written < size) {
if (size - written > (int)sizeof(buf)) if (size - written > (int)sizeof(buf))
count = sizeof buf; count = sizeof buf;
else else
count = size - written; count = size - written;
ret = read(infd, buf, count); ret = read(infd, buf, count);
if (!ret) if (!ret)
return COPY_FILE_READ_EOF; return COPY_FILE_READ_EOF;
if (ret < 0) if (ret < 0)
return COPY_FILE_READ_ERROR; return COPY_FILE_READ_ERROR;
/* acumulate crc32 if requested */ /* acumulate crc32 if requested */
if (crc32) if (crc32)
*crc32 = Crc32_ComputeBuf(*crc32, buf, ret); *crc32 = Crc32_ComputeBuf(*crc32, buf, ret);
if (!write_all(outfd, buf, ret)) if (!write_all(outfd, buf, ret))
return COPY_FILE_WRITE_ERROR; return COPY_FILE_WRITE_ERROR;
if (notify_progress_func != NULL) if (notify_progress_func != NULL)
notify_progress_func(ret, 0); notify_progress_func(ret, 0);
written += ret; written += ret;
} }
return COPY_FILE_OK; return COPY_FILE_OK;
} }
const char * copy_file_status_to_str(int status) const char * copy_file_status_to_str(int status)
{ {
switch (status) { switch (status) {
case COPY_FILE_OK: return "OK"; case COPY_FILE_OK: return "OK";
case COPY_FILE_READ_EOF: return "Unexpected end of data while reading"; case COPY_FILE_READ_EOF: return "Unexpected end of data while reading";
case COPY_FILE_READ_ERROR: return "Error reading"; case COPY_FILE_READ_ERROR: return "Error reading";
case COPY_FILE_WRITE_ERROR: return "Error writing"; case COPY_FILE_WRITE_ERROR: return "Error writing";
default: return "????????"; default: return "????????";
} }
} }

View File

@ -28,56 +28,56 @@
static do_exec_t *exec_func = NULL; static do_exec_t *exec_func = NULL;
void register_exec_func(do_exec_t *func) { void register_exec_func(do_exec_t *func) {
exec_func = func; exec_func = func;
} }
void fix_fds(int fdin, int fdout, int fderr) void fix_fds(int fdin, int fdout, int fderr)
{ {
int i; int i;
for (i = 0; i < 256; i++) for (i = 0; i < 256; i++)
if (i != fdin && i != fdout && i != fderr) if (i != fdin && i != fdout && i != fderr)
close(i); close(i);
dup2(fdin, 0); dup2(fdin, 0);
dup2(fdout, 1); dup2(fdout, 1);
dup2(fderr, 2); dup2(fderr, 2);
close(fdin); close(fdin);
close(fdout); close(fdout);
if (fderr != 2) if (fderr != 2)
close(fderr); close(fderr);
} }
void do_fork_exec(const char *cmdline, int *pid, int *stdin_fd, int *stdout_fd, void do_fork_exec(const char *cmdline, int *pid, int *stdin_fd, int *stdout_fd,
int *stderr_fd) int *stderr_fd)
{ {
int inpipe[2], outpipe[2], errpipe[2]; int inpipe[2], outpipe[2], errpipe[2];
if (socketpair(AF_UNIX, SOCK_STREAM, 0, inpipe) || if (socketpair(AF_UNIX, SOCK_STREAM, 0, inpipe) ||
socketpair(AF_UNIX, SOCK_STREAM, 0, outpipe) || socketpair(AF_UNIX, SOCK_STREAM, 0, outpipe) ||
(stderr_fd && socketpair(AF_UNIX, SOCK_STREAM, 0, errpipe))) { (stderr_fd && socketpair(AF_UNIX, SOCK_STREAM, 0, errpipe))) {
perror("socketpair"); perror("socketpair");
exit(1); exit(1);
} }
switch (*pid = fork()) { switch (*pid = fork()) {
case -1: case -1:
perror("fork"); perror("fork");
exit(-1); exit(-1);
case 0: case 0:
if (stderr_fd) { if (stderr_fd) {
fix_fds(inpipe[0], outpipe[1], errpipe[1]); fix_fds(inpipe[0], outpipe[1], errpipe[1]);
} else } else
fix_fds(inpipe[0], outpipe[1], 2); fix_fds(inpipe[0], outpipe[1], 2);
if (exec_func != NULL) if (exec_func != NULL)
exec_func(cmdline); exec_func(cmdline);
exit(-1); exit(-1);
default:; default:;
} }
close(inpipe[0]); close(inpipe[0]);
close(outpipe[1]); close(outpipe[1]);
*stdin_fd = inpipe[1]; *stdin_fd = inpipe[1];
*stdout_fd = outpipe[0]; *stdout_fd = outpipe[0];
if (stderr_fd) { if (stderr_fd) {
close(errpipe[1]); close(errpipe[1]);
*stderr_fd = errpipe[0]; *stderr_fd = errpipe[0];
} }
} }

View File

@ -27,90 +27,90 @@
void perror_wrapper(const char * msg) void perror_wrapper(const char * msg)
{ {
int prev=errno; int prev=errno;
perror(msg); perror(msg);
errno=prev; errno=prev;
} }
void set_nonblock(int fd) void set_nonblock(int fd)
{ {
int fl = fcntl(fd, F_GETFL, 0); int fl = fcntl(fd, F_GETFL, 0);
if (fl & O_NONBLOCK) if (fl & O_NONBLOCK)
return; return;
fcntl(fd, F_SETFL, fl | O_NONBLOCK); fcntl(fd, F_SETFL, fl | O_NONBLOCK);
} }
void set_block(int fd) void set_block(int fd)
{ {
int fl = fcntl(fd, F_GETFL, 0); int fl = fcntl(fd, F_GETFL, 0);
if (!(fl & O_NONBLOCK)) if (!(fl & O_NONBLOCK))
return; return;
fcntl(fd, F_SETFL, fl & ~O_NONBLOCK); fcntl(fd, F_SETFL, fl & ~O_NONBLOCK);
} }
int write_all(int fd, const void *buf, int size) int write_all(int fd, const void *buf, int size)
{ {
int written = 0; int written = 0;
int ret; int ret;
while (written < size) { while (written < size) {
ret = write(fd, (char *) buf + written, size - written); ret = write(fd, (char *) buf + written, size - written);
if (ret == -1 && errno == EINTR) if (ret == -1 && errno == EINTR)
continue; continue;
if (ret <= 0) { if (ret <= 0) {
return 0; return 0;
} }
written += ret; written += ret;
} }
// fprintf(stderr, "sent %d bytes\n", size); // fprintf(stderr, "sent %d bytes\n", size);
return 1; return 1;
} }
int read_all(int fd, void *buf, int size) int read_all(int fd, void *buf, int size)
{ {
int got_read = 0; int got_read = 0;
int ret; int ret;
while (got_read < size) { while (got_read < size) {
ret = read(fd, (char *) buf + got_read, size - got_read); ret = read(fd, (char *) buf + got_read, size - got_read);
if (ret == -1 && errno == EINTR) if (ret == -1 && errno == EINTR)
continue; continue;
if (ret == 0) { if (ret == 0) {
errno = 0; errno = 0;
fprintf(stderr, "EOF\n"); fprintf(stderr, "EOF\n");
return 0; return 0;
} }
if (ret < 0) { if (ret < 0) {
if (errno != EAGAIN) if (errno != EAGAIN)
perror_wrapper("read"); perror_wrapper("read");
return 0; return 0;
} }
if (got_read == 0) { if (got_read == 0) {
// force blocking operation on further reads // force blocking operation on further reads
set_block(fd); set_block(fd);
} }
got_read += ret; got_read += ret;
} }
// fprintf(stderr, "read %d bytes\n", size); // fprintf(stderr, "read %d bytes\n", size);
return 1; return 1;
} }
int copy_fd_all(int fdout, int fdin) int copy_fd_all(int fdout, int fdin)
{ {
int ret; int ret;
char buf[4096]; char buf[4096];
for (;;) { for (;;) {
ret = read(fdin, buf, sizeof(buf)); ret = read(fdin, buf, sizeof(buf));
if (ret == -1 && errno == EINTR) if (ret == -1 && errno == EINTR)
continue; continue;
if (!ret) if (!ret)
break; break;
if (ret < 0) { if (ret < 0) {
perror_wrapper("read"); perror_wrapper("read");
return 0; return 0;
} }
if (!write_all(fdout, buf, ret)) { if (!write_all(fdout, buf, ret)) {
perror_wrapper("write"); perror_wrapper("write");
return 0; return 0;
} }
} }
return 1; return 1;
} }

View File

@ -24,8 +24,8 @@
#include <libvchan.h> #include <libvchan.h>
struct buffer { struct buffer {
char *data; char *data;
int buflen; int buflen;
}; };
/* return codes for buffered writes */ /* return codes for buffered writes */
@ -48,7 +48,7 @@ int write_stdin(int fd, const char *data, int len, struct buffer *buffer);
int fork_and_flush_stdin(int fd, struct buffer *buffer); int fork_and_flush_stdin(int fd, struct buffer *buffer);
void do_fork_exec(const char *cmdline, int *pid, int *stdin_fd, int *stdout_fd, void do_fork_exec(const char *cmdline, int *pid, int *stdin_fd, int *stdout_fd,
int *stderr_fd); int *stderr_fd);
void wait_for_vchan_or_argfd(libvchan_t *vchan, int max, fd_set * rdset, fd_set * wrset); void wait_for_vchan_or_argfd(libvchan_t *vchan, int max, fd_set * rdset, fd_set * wrset);
int read_vchan_all(libvchan_t *vchan, void *data, size_t size); int read_vchan_all(libvchan_t *vchan, void *data, size_t size);
int write_vchan_all(libvchan_t *vchan, const void *data, size_t size); int write_vchan_all(libvchan_t *vchan, const void *data, size_t size);

View File

@ -35,32 +35,32 @@
#include <sys/types.h> #include <sys/types.h>
struct file_header { struct file_header {
unsigned int namelen; unsigned int namelen;
unsigned int mode; unsigned int mode;
unsigned long long filelen; unsigned long long filelen;
unsigned int atime; unsigned int atime;
unsigned int atime_nsec; unsigned int atime_nsec;
unsigned int mtime; unsigned int mtime;
unsigned int mtime_nsec; unsigned int mtime_nsec;
}; };
struct result_header { struct result_header {
uint32_t error_code; uint32_t error_code;
uint32_t _pad; uint32_t _pad;
uint64_t crc32; uint64_t crc32;
} __attribute__((packed)); } __attribute__((packed));
/* optional info about last processed file */ /* optional info about last processed file */
struct result_header_ext { struct result_header_ext {
uint32_t last_namelen; uint32_t last_namelen;
char last_name[0]; char last_name[0];
} __attribute__((packed)); } __attribute__((packed));
enum { enum {
COPY_FILE_OK, COPY_FILE_OK,
COPY_FILE_READ_EOF, COPY_FILE_READ_EOF,
COPY_FILE_READ_ERROR, COPY_FILE_READ_ERROR,
COPY_FILE_WRITE_ERROR COPY_FILE_WRITE_ERROR
}; };
/* feedback handling */ /* feedback handling */

View File

@ -34,91 +34,91 @@ _Noreturn static void call_error_handler(const char *fmt, ...)
static int write_all_with_crc(int fd, const void *buf, int size) static int write_all_with_crc(int fd, const void *buf, int size)
{ {
crc32_sum = Crc32_ComputeBuf(crc32_sum, buf, size); crc32_sum = Crc32_ComputeBuf(crc32_sum, buf, size);
return write_all(fd, buf, size); return write_all(fd, buf, size);
} }
void notify_end_and_wait_for_result(void) void notify_end_and_wait_for_result(void)
{ {
struct file_header end_hdr; struct file_header end_hdr;
/* nofity end of transfer */ /* nofity end of transfer */
memset(&end_hdr, 0, sizeof(end_hdr)); memset(&end_hdr, 0, sizeof(end_hdr));
end_hdr.namelen = 0; end_hdr.namelen = 0;
end_hdr.filelen = 0; end_hdr.filelen = 0;
write_all_with_crc(1, &end_hdr, sizeof(end_hdr)); write_all_with_crc(1, &end_hdr, sizeof(end_hdr));
set_block(0); set_block(0);
wait_for_result(); wait_for_result();
} }
void wait_for_result(void) void wait_for_result(void)
{ {
struct result_header hdr; struct result_header hdr;
struct result_header_ext hdr_ext; struct result_header_ext hdr_ext;
char last_filename[MAX_PATH_LENGTH + 1]; char last_filename[MAX_PATH_LENGTH + 1];
char last_filename_prefix[] = "; Last file: "; char last_filename_prefix[] = "; Last file: ";
if (!read_all(0, &hdr, sizeof(hdr))) { if (!read_all(0, &hdr, sizeof(hdr))) {
if (errno == EAGAIN) { if (errno == EAGAIN) {
// no result sent and stdin still open // no result sent and stdin still open
return; return;
} else { } else {
// other read error or EOF // other read error or EOF
exit(1); // hopefully remote has produced error message exit(1); // hopefully remote has produced error message
} }
} }
if (!read_all(0, &hdr_ext, sizeof(hdr_ext))) { if (!read_all(0, &hdr_ext, sizeof(hdr_ext))) {
// remote used old result_header struct // remote used old result_header struct
hdr_ext.last_namelen = 0; hdr_ext.last_namelen = 0;
} }
if (hdr_ext.last_namelen > MAX_PATH_LENGTH) { if (hdr_ext.last_namelen > MAX_PATH_LENGTH) {
// read only at most MAX_PATH_LENGTH chars // read only at most MAX_PATH_LENGTH chars
hdr_ext.last_namelen = MAX_PATH_LENGTH; hdr_ext.last_namelen = MAX_PATH_LENGTH;
} }
if (!read_all(0, last_filename, hdr_ext.last_namelen)) { if (!read_all(0, last_filename, hdr_ext.last_namelen)) {
fprintf(stderr, "Failed to get last filename\n"); fprintf(stderr, "Failed to get last filename\n");
hdr_ext.last_namelen = 0; hdr_ext.last_namelen = 0;
} }
last_filename[hdr_ext.last_namelen] = '\0'; last_filename[hdr_ext.last_namelen] = '\0';
if (!hdr_ext.last_namelen) if (!hdr_ext.last_namelen)
/* set prefix to empty string */ /* set prefix to empty string */
last_filename_prefix[0] = '\0'; last_filename_prefix[0] = '\0';
errno = hdr.error_code; errno = hdr.error_code;
if (hdr.error_code != 0) { if (hdr.error_code != 0) {
switch (hdr.error_code) { switch (hdr.error_code) {
case EEXIST: case EEXIST:
call_error_handler("File copy: not overwriting existing file. Clean QubesIncoming dir, and retry copy%s%s", last_filename_prefix, last_filename); call_error_handler("File copy: not overwriting existing file. Clean QubesIncoming dir, and retry copy%s%s", last_filename_prefix, last_filename);
break; break;
case EINVAL: case EINVAL:
call_error_handler("File copy: Corrupted data from packer%s%s", last_filename_prefix, last_filename); call_error_handler("File copy: Corrupted data from packer%s%s", last_filename_prefix, last_filename);
break; break;
case EDQUOT: case EDQUOT:
if (ignore_quota_error) { if (ignore_quota_error) {
/* skip also CRC check as sender and receiver might be /* skip also CRC check as sender and receiver might be
* desynchronized in this case */ * desynchronized in this case */
return; return;
} }
/* fall though */ /* fall though */
default: default:
call_error_handler("File copy: %s%s%s", call_error_handler("File copy: %s%s%s",
strerror(hdr.error_code), last_filename_prefix, last_filename); strerror(hdr.error_code), last_filename_prefix, last_filename);
} }
} }
if (hdr.crc32 != crc32_sum) { if (hdr.crc32 != crc32_sum) {
call_error_handler("File transfer failed: checksum mismatch"); call_error_handler("File transfer failed: checksum mismatch");
} }
} }
void write_headers(const struct file_header *hdr, const char *filename) void write_headers(const struct file_header *hdr, const char *filename)
{ {
if (!write_all_with_crc(1, hdr, sizeof(*hdr)) if (!write_all_with_crc(1, hdr, sizeof(*hdr))
|| !write_all_with_crc(1, filename, hdr->namelen)) { || !write_all_with_crc(1, filename, hdr->namelen)) {
set_block(0); set_block(0);
wait_for_result(); wait_for_result();
exit(1); exit(1);
} }
} }
int copy_file_with_crc(int outfd, int infd, long long size) { int copy_file_with_crc(int outfd, int infd, long long size) {
@ -127,100 +127,100 @@ int copy_file_with_crc(int outfd, int infd, long long size) {
int single_file_processor(const char *filename, const struct stat *st) int single_file_processor(const char *filename, const struct stat *st)
{ {
struct file_header hdr; struct file_header hdr;
int fd; int fd;
mode_t mode = st->st_mode; mode_t mode = st->st_mode;
hdr.namelen = strlen(filename) + 1; hdr.namelen = strlen(filename) + 1;
hdr.mode = mode; hdr.mode = mode;
hdr.atime = st->st_atim.tv_sec; hdr.atime = st->st_atim.tv_sec;
hdr.atime_nsec = st->st_atim.tv_nsec; hdr.atime_nsec = st->st_atim.tv_nsec;
hdr.mtime = st->st_mtim.tv_sec; hdr.mtime = st->st_mtim.tv_sec;
hdr.mtime_nsec = st->st_mtim.tv_nsec; hdr.mtime_nsec = st->st_mtim.tv_nsec;
if (S_ISREG(mode)) { if (S_ISREG(mode)) {
int ret; int ret;
fd = open(filename, O_RDONLY); fd = open(filename, O_RDONLY);
if (fd < 0) if (fd < 0)
call_error_handler("open %s", filename); call_error_handler("open %s", filename);
hdr.filelen = st->st_size; hdr.filelen = st->st_size;
write_headers(&hdr, filename); write_headers(&hdr, filename);
ret = copy_file(1, fd, hdr.filelen, &crc32_sum); ret = copy_file(1, fd, hdr.filelen, &crc32_sum);
if (ret != COPY_FILE_OK) { if (ret != COPY_FILE_OK) {
if (ret != COPY_FILE_WRITE_ERROR) if (ret != COPY_FILE_WRITE_ERROR)
call_error_handler("Copying file %s: %s", filename, call_error_handler("Copying file %s: %s", filename,
copy_file_status_to_str(ret)); copy_file_status_to_str(ret));
else { else {
set_block(0); set_block(0);
wait_for_result(); wait_for_result();
exit(1); exit(1);
} }
} }
close(fd); close(fd);
} }
if (S_ISDIR(mode)) { if (S_ISDIR(mode)) {
hdr.filelen = 0; hdr.filelen = 0;
write_headers(&hdr, filename); write_headers(&hdr, filename);
} }
if (S_ISLNK(mode)) { if (S_ISLNK(mode)) {
char name[st->st_size + 1]; char name[st->st_size + 1];
if (readlink(filename, name, sizeof(name)) != st->st_size) if (readlink(filename, name, sizeof(name)) != st->st_size)
call_error_handler("readlink %s", filename); call_error_handler("readlink %s", filename);
hdr.filelen = st->st_size; hdr.filelen = st->st_size;
write_headers(&hdr, filename); write_headers(&hdr, filename);
if (!write_all_with_crc(1, name, st->st_size)) { if (!write_all_with_crc(1, name, st->st_size)) {
set_block(0); set_block(0);
wait_for_result(); wait_for_result();
exit(1); exit(1);
} }
} }
// check for possible error from qfile-unpacker // check for possible error from qfile-unpacker
wait_for_result(); wait_for_result();
return 0; return 0;
} }
int do_fs_walk(const char *file, int ignore_symlinks) int do_fs_walk(const char *file, int ignore_symlinks)
{ {
char *newfile; char *newfile;
struct stat st; struct stat st;
struct dirent *ent; struct dirent *ent;
DIR *dir; DIR *dir;
if (lstat(file, &st)) if (lstat(file, &st))
call_error_handler("stat %s", file); call_error_handler("stat %s", file);
if (S_ISLNK(st.st_mode) && ignore_symlinks) if (S_ISLNK(st.st_mode) && ignore_symlinks)
return 0; return 0;
single_file_processor(file, &st); single_file_processor(file, &st);
if (!S_ISDIR(st.st_mode)) if (!S_ISDIR(st.st_mode))
return 0; return 0;
dir = opendir(file); dir = opendir(file);
if (!dir) if (!dir)
call_error_handler("opendir %s", file); call_error_handler("opendir %s", file);
while ((ent = readdir(dir))) { while ((ent = readdir(dir))) {
char *fname = ent->d_name; char *fname = ent->d_name;
if (!strcmp(fname, ".") || !strcmp(fname, "..")) if (!strcmp(fname, ".") || !strcmp(fname, ".."))
continue; continue;
if (asprintf(&newfile, "%s/%s", file, fname) >= 0) { if (asprintf(&newfile, "%s/%s", file, fname) >= 0) {
do_fs_walk(newfile, ignore_symlinks); do_fs_walk(newfile, ignore_symlinks);
free(newfile); free(newfile);
} else { } else {
fprintf(stderr, "asprintf failed\n"); fprintf(stderr, "asprintf failed\n");
exit(1); exit(1);
} }
} }
closedir(dir); closedir(dir);
// directory metadata is resent; this makes the code simple, // directory metadata is resent; this makes the code simple,
// and the atime/mtime is set correctly at the second time // and the atime/mtime is set correctly at the second time
single_file_processor(file, &st); single_file_processor(file, &st);
return 0; return 0;
} }
void qfile_pack_init(void) { void qfile_pack_init(void) {
crc32_sum = 0; crc32_sum = 0;
ignore_quota_error = 0; ignore_quota_error = 0;
// this will allow checking for possible feedback packet in the middle of transfer // this will allow checking for possible feedback packet in the middle of transfer
set_nonblock(0); set_nonblock(0);
signal(SIGPIPE, SIG_IGN); signal(SIGPIPE, SIG_IGN);
error_handler = NULL; error_handler = NULL;
} }

View File

@ -28,51 +28,51 @@
int wait_for_vchan_or_argfd_once(libvchan_t *ctrl, int max, fd_set * rdset, fd_set * wrset) int wait_for_vchan_or_argfd_once(libvchan_t *ctrl, int max, fd_set * rdset, fd_set * wrset)
{ {
int vfd, ret; int vfd, ret;
struct timespec tv = { 1, 100000000 }; struct timespec tv = { 1, 100000000 };
sigset_t empty_set; sigset_t empty_set;
sigemptyset(&empty_set); sigemptyset(&empty_set);
vfd = libvchan_fd_for_select(ctrl); vfd = libvchan_fd_for_select(ctrl);
FD_SET(vfd, rdset); FD_SET(vfd, rdset);
if (vfd > max) if (vfd > max)
max = vfd; max = vfd;
max++; max++;
ret = pselect(max, rdset, wrset, NULL, &tv, &empty_set); ret = pselect(max, rdset, wrset, NULL, &tv, &empty_set);
if (ret < 0) { if (ret < 0) {
if (errno != EINTR) { if (errno != EINTR) {
perror("select"); perror("select");
exit(1); exit(1);
} else { } else {
FD_ZERO(rdset); FD_ZERO(rdset);
FD_ZERO(wrset); FD_ZERO(wrset);
fprintf(stderr, "eintr\n"); fprintf(stderr, "eintr\n");
return 1; return 1;
} }
} }
if (!libvchan_is_open(ctrl)) { if (!libvchan_is_open(ctrl)) {
fprintf(stderr, "libvchan_is_eof\n"); fprintf(stderr, "libvchan_is_eof\n");
exit(0); exit(0);
} }
if (FD_ISSET(vfd, rdset)) if (FD_ISSET(vfd, rdset))
// the following will never block; we need to do this to // the following will never block; we need to do this to
// clear libvchan_fd pending state // clear libvchan_fd pending state
libvchan_wait(ctrl); libvchan_wait(ctrl);
if (libvchan_data_ready(ctrl)) if (libvchan_data_ready(ctrl))
return 1; return 1;
return ret; return ret;
} }
void wait_for_vchan_or_argfd(libvchan_t *ctrl, int max, fd_set * rdset, fd_set * wrset) void wait_for_vchan_or_argfd(libvchan_t *ctrl, int max, fd_set * rdset, fd_set * wrset)
{ {
fd_set r = *rdset, w = *wrset; fd_set r = *rdset, w = *wrset;
do { do {
*rdset = r; *rdset = r;
*wrset = w; *wrset = w;
} }
while (wait_for_vchan_or_argfd_once(ctrl, max, rdset, wrset) == 0); while (wait_for_vchan_or_argfd_once(ctrl, max, rdset, wrset) == 0);
} }
int write_vchan_all(libvchan_t *vchan, const void *data, size_t size) { int write_vchan_all(libvchan_t *vchan, const void *data, size_t size) {

View File

@ -29,45 +29,45 @@
int get_server_socket(const char *socket_address) int get_server_socket(const char *socket_address)
{ {
struct sockaddr_un sockname; struct sockaddr_un sockname;
int s; int s;
unlink(socket_address); unlink(socket_address);
s = socket(AF_UNIX, SOCK_STREAM, 0); s = socket(AF_UNIX, SOCK_STREAM, 0);
if (s < 0) { if (s < 0) {
printf("socket() failed\n"); printf("socket() failed\n");
exit(1); exit(1);
} }
memset(&sockname, 0, sizeof(sockname)); memset(&sockname, 0, sizeof(sockname));
sockname.sun_family = AF_UNIX; sockname.sun_family = AF_UNIX;
strncpy(sockname.sun_path, socket_address, sizeof sockname.sun_path); strncpy(sockname.sun_path, socket_address, sizeof sockname.sun_path);
sockname.sun_path[sizeof sockname.sun_path - 1] = 0; sockname.sun_path[sizeof sockname.sun_path - 1] = 0;
if (bind(s, (struct sockaddr *) &sockname, sizeof(sockname)) == -1) { if (bind(s, (struct sockaddr *) &sockname, sizeof(sockname)) == -1) {
printf("bind() failed\n"); printf("bind() failed\n");
close(s); close(s);
exit(1); exit(1);
} }
// chmod(sockname.sun_path, 0666); // chmod(sockname.sun_path, 0666);
if (listen(s, 5) == -1) { if (listen(s, 5) == -1) {
perror("listen() failed\n"); perror("listen() failed\n");
close(s); close(s);
exit(1); exit(1);
} }
return s; return s;
} }
int do_accept(int s) int do_accept(int s)
{ {
struct sockaddr_un peer; struct sockaddr_un peer;
unsigned int addrlen; unsigned int addrlen;
int fd; int fd;
addrlen = sizeof(peer); addrlen = sizeof(peer);
fd = accept(s, (struct sockaddr *) &peer, &addrlen); fd = accept(s, (struct sockaddr *) &peer, &addrlen);
if (fd == -1) { if (fd == -1) {
perror("unix accept"); perror("unix accept");
exit(1); exit(1);
} }
return fd; return fd;
} }

View File

@ -39,206 +39,206 @@ void send_status_and_crc(int code, const char *last_filename);
void do_exit(int code, const char *last_filename) void do_exit(int code, const char *last_filename)
{ {
close(0); close(0);
send_status_and_crc(code, last_filename); send_status_and_crc(code, last_filename);
exit(code); exit(code);
} }
void set_size_limit(unsigned long long new_bytes_limit, unsigned long long new_files_limit) void set_size_limit(unsigned long long new_bytes_limit, unsigned long long new_files_limit)
{ {
bytes_limit = new_bytes_limit; bytes_limit = new_bytes_limit;
files_limit = new_files_limit; files_limit = new_files_limit;
} }
void set_verbose(int value) void set_verbose(int value)
{ {
verbose = value; verbose = value;
} }
void set_procfs_fd(int value) void set_procfs_fd(int value)
{ {
procdir_fd = value; procdir_fd = value;
use_tmpfile = 1; use_tmpfile = 1;
} }
unsigned long crc32_sum = 0; unsigned long crc32_sum = 0;
int read_all_with_crc(int fd, void *buf, int size) { int read_all_with_crc(int fd, void *buf, int size) {
int ret; int ret;
ret = read_all(fd, buf, size); ret = read_all(fd, buf, size);
if (ret) if (ret)
crc32_sum = Crc32_ComputeBuf(crc32_sum, buf, size); crc32_sum = Crc32_ComputeBuf(crc32_sum, buf, size);
return ret; return ret;
} }
void send_status_and_crc(int code, const char *last_filename) { void send_status_and_crc(int code, const char *last_filename) {
struct result_header hdr; struct result_header hdr;
struct result_header_ext hdr_ext; struct result_header_ext hdr_ext;
int saved_errno; int saved_errno;
saved_errno = errno; saved_errno = errno;
hdr.error_code = code; hdr.error_code = code;
hdr.crc32 = crc32_sum; hdr.crc32 = crc32_sum;
if (!write_all(1, &hdr, sizeof(hdr))) if (!write_all(1, &hdr, sizeof(hdr)))
perror("write status"); perror("write status");
if (last_filename) { if (last_filename) {
hdr_ext.last_namelen = strlen(last_filename); hdr_ext.last_namelen = strlen(last_filename);
if (!write_all(1, &hdr_ext, sizeof(hdr_ext))) if (!write_all(1, &hdr_ext, sizeof(hdr_ext)))
perror("write status ext"); perror("write status ext");
if (!write_all(1, last_filename, hdr_ext.last_namelen)) if (!write_all(1, last_filename, hdr_ext.last_namelen))
perror("write last_filename"); perror("write last_filename");
} }
errno = saved_errno; errno = saved_errno;
} }
void fix_times_and_perms(struct file_header *untrusted_hdr, void fix_times_and_perms(struct file_header *untrusted_hdr,
const char *untrusted_name) const char *untrusted_name)
{ {
struct timeval times[2] = struct timeval times[2] =
{ {untrusted_hdr->atime, untrusted_hdr->atime_nsec / 1000}, {
{untrusted_hdr->mtime, {untrusted_hdr->atime, untrusted_hdr->atime_nsec / 1000},
untrusted_hdr->mtime_nsec / 1000} {untrusted_hdr->mtime, untrusted_hdr->mtime_nsec / 1000}
}; };
if (chmod(untrusted_name, untrusted_hdr->mode & 07777)) /* safe because of chroot */ if (chmod(untrusted_name, untrusted_hdr->mode & 07777)) /* safe because of chroot */
do_exit(errno, untrusted_name); do_exit(errno, untrusted_name);
if (utimes(untrusted_name, times)) /* as above */ if (utimes(untrusted_name, times)) /* as above */
do_exit(errno, untrusted_name); do_exit(errno, untrusted_name);
} }
void process_one_file_reg(struct file_header *untrusted_hdr, void process_one_file_reg(struct file_header *untrusted_hdr,
const char *untrusted_name) const char *untrusted_name)
{ {
int ret; int ret;
int fdout = -1; int fdout = -1;
/* make the file inaccessible until fully written */ /* make the file inaccessible until fully written */
if (use_tmpfile) { if (use_tmpfile) {
fdout = open(".", O_WRONLY | O_TMPFILE, 0700); fdout = open(".", O_WRONLY | O_TMPFILE, 0700);
if (fdout < 0) { if (fdout < 0) {
if (errno==ENOENT || /* most likely, kernel too old for O_TMPFILE */ if (errno==ENOENT || /* most likely, kernel too old for O_TMPFILE */
errno==EOPNOTSUPP) /* filesystem has no support for O_TMPFILE */ errno==EOPNOTSUPP) /* filesystem has no support for O_TMPFILE */
use_tmpfile = 0; use_tmpfile = 0;
else else
do_exit(errno, untrusted_name); do_exit(errno, untrusted_name);
} }
} }
if (fdout < 0) if (fdout < 0)
fdout = open(untrusted_name, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, 0000); /* safe because of chroot */ fdout = open(untrusted_name, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, 0000); /* safe because of chroot */
if (fdout < 0) if (fdout < 0)
do_exit(errno, untrusted_name); do_exit(errno, untrusted_name);
/* sizes are signed elsewhere */ /* sizes are signed elsewhere */
if (untrusted_hdr->filelen > LLONG_MAX || (bytes_limit && untrusted_hdr->filelen > bytes_limit)) if (untrusted_hdr->filelen > LLONG_MAX || (bytes_limit && untrusted_hdr->filelen > bytes_limit))
do_exit(EDQUOT, untrusted_name); do_exit(EDQUOT, untrusted_name);
if (bytes_limit && total_bytes > bytes_limit - untrusted_hdr->filelen) if (bytes_limit && total_bytes > bytes_limit - untrusted_hdr->filelen)
do_exit(EDQUOT, untrusted_name); do_exit(EDQUOT, untrusted_name);
total_bytes += untrusted_hdr->filelen; total_bytes += untrusted_hdr->filelen;
ret = copy_file(fdout, 0, untrusted_hdr->filelen, &crc32_sum); ret = copy_file(fdout, 0, untrusted_hdr->filelen, &crc32_sum);
if (ret != COPY_FILE_OK) { if (ret != COPY_FILE_OK) {
if (ret == COPY_FILE_READ_EOF if (ret == COPY_FILE_READ_EOF
|| ret == COPY_FILE_READ_ERROR) || ret == COPY_FILE_READ_ERROR)
do_exit(LEGAL_EOF, untrusted_name); // hopefully remote will produce error message do_exit(LEGAL_EOF, untrusted_name); // hopefully remote will produce error message
else else
do_exit(errno, untrusted_name); do_exit(errno, untrusted_name);
} }
if (use_tmpfile) { if (use_tmpfile) {
char fd_str[7]; char fd_str[7];
snprintf(fd_str, sizeof(fd_str), "%d", fdout); snprintf(fd_str, sizeof(fd_str), "%d", fdout);
if (linkat(procdir_fd, fd_str, AT_FDCWD, untrusted_name, AT_SYMLINK_FOLLOW) < 0) if (linkat(procdir_fd, fd_str, AT_FDCWD, untrusted_name, AT_SYMLINK_FOLLOW) < 0)
do_exit(errno, untrusted_name); do_exit(errno, untrusted_name);
} }
close(fdout); close(fdout);
fix_times_and_perms(untrusted_hdr, untrusted_name); fix_times_and_perms(untrusted_hdr, untrusted_name);
} }
void process_one_file_dir(struct file_header *untrusted_hdr, void process_one_file_dir(struct file_header *untrusted_hdr,
const char *untrusted_name) const char *untrusted_name)
{ {
// fix perms only when the directory is sent for the second time // fix perms only when the directory is sent for the second time
// it allows to transfer r.x directory contents, as we create it rwx initially // it allows to transfer r.x directory contents, as we create it rwx initially
struct stat buf; struct stat buf;
if (!mkdir(untrusted_name, 0700)) /* safe because of chroot */ if (!mkdir(untrusted_name, 0700)) /* safe because of chroot */
return; return;
if (errno != EEXIST) if (errno != EEXIST)
do_exit(errno, untrusted_name); do_exit(errno, untrusted_name);
if (stat(untrusted_name,&buf) < 0) if (stat(untrusted_name,&buf) < 0)
do_exit(errno, untrusted_name); do_exit(errno, untrusted_name);
total_bytes += buf.st_size; total_bytes += buf.st_size;
/* size accumulated after the fact, so don't check limit here */ /* size accumulated after the fact, so don't check limit here */
fix_times_and_perms(untrusted_hdr, untrusted_name); fix_times_and_perms(untrusted_hdr, untrusted_name);
} }
void process_one_file_link(struct file_header *untrusted_hdr, void process_one_file_link(struct file_header *untrusted_hdr,
const char *untrusted_name) const char *untrusted_name)
{ {
char untrusted_content[MAX_PATH_LENGTH]; char untrusted_content[MAX_PATH_LENGTH];
unsigned int filelen; unsigned int filelen;
if (untrusted_hdr->filelen > MAX_PATH_LENGTH - 1) if (untrusted_hdr->filelen > MAX_PATH_LENGTH - 1)
do_exit(ENAMETOOLONG, untrusted_name); do_exit(ENAMETOOLONG, untrusted_name);
filelen = untrusted_hdr->filelen; /* sanitized above */ filelen = untrusted_hdr->filelen; /* sanitized above */
total_bytes += filelen; total_bytes += filelen;
if (bytes_limit && total_bytes > bytes_limit) if (bytes_limit && total_bytes > bytes_limit)
do_exit(EDQUOT, untrusted_name); do_exit(EDQUOT, untrusted_name);
if (!read_all_with_crc(0, untrusted_content, filelen)) if (!read_all_with_crc(0, untrusted_content, filelen))
do_exit(LEGAL_EOF, untrusted_name); // hopefully remote has produced error message do_exit(LEGAL_EOF, untrusted_name); // hopefully remote has produced error message
untrusted_content[filelen] = 0; untrusted_content[filelen] = 0;
if (symlink(untrusted_content, untrusted_name)) /* safe because of chroot */ if (symlink(untrusted_content, untrusted_name)) /* safe because of chroot */
do_exit(errno, untrusted_name); do_exit(errno, untrusted_name);
} }
void process_one_file(struct file_header *untrusted_hdr) void process_one_file(struct file_header *untrusted_hdr)
{ {
unsigned int namelen; unsigned int namelen;
if (untrusted_hdr->namelen > MAX_PATH_LENGTH - 1) if (untrusted_hdr->namelen > MAX_PATH_LENGTH - 1)
do_exit(ENAMETOOLONG, NULL); /* filename too long so not received at all */ do_exit(ENAMETOOLONG, NULL); /* filename too long so not received at all */
namelen = untrusted_hdr->namelen; /* sanitized above */ namelen = untrusted_hdr->namelen; /* sanitized above */
if (!read_all_with_crc(0, untrusted_namebuf, namelen)) if (!read_all_with_crc(0, untrusted_namebuf, namelen))
do_exit(LEGAL_EOF, NULL); // hopefully remote has produced error message do_exit(LEGAL_EOF, NULL); // hopefully remote has produced error message
untrusted_namebuf[namelen] = 0; untrusted_namebuf[namelen] = 0;
if (S_ISREG(untrusted_hdr->mode)) if (S_ISREG(untrusted_hdr->mode))
process_one_file_reg(untrusted_hdr, untrusted_namebuf); process_one_file_reg(untrusted_hdr, untrusted_namebuf);
else if (S_ISLNK(untrusted_hdr->mode)) else if (S_ISLNK(untrusted_hdr->mode))
process_one_file_link(untrusted_hdr, untrusted_namebuf); process_one_file_link(untrusted_hdr, untrusted_namebuf);
else if (S_ISDIR(untrusted_hdr->mode)) else if (S_ISDIR(untrusted_hdr->mode))
process_one_file_dir(untrusted_hdr, untrusted_namebuf); process_one_file_dir(untrusted_hdr, untrusted_namebuf);
else else
do_exit(EINVAL, untrusted_namebuf); do_exit(EINVAL, untrusted_namebuf);
if (verbose && !S_ISDIR(untrusted_hdr->mode)) if (verbose && !S_ISDIR(untrusted_hdr->mode))
fprintf(stderr, "%s\n", untrusted_namebuf); fprintf(stderr, "%s\n", untrusted_namebuf);
} }
int do_unpack(void) int do_unpack(void)
{ {
struct file_header untrusted_hdr; struct file_header untrusted_hdr;
#ifdef HAVE_SYNCFS #ifdef HAVE_SYNCFS
int cwd_fd; int cwd_fd;
int saved_errno; int saved_errno;
#endif #endif
total_bytes = total_files = 0; total_bytes = total_files = 0;
/* initialize checksum */ /* initialize checksum */
crc32_sum = 0; crc32_sum = 0;
while (read_all_with_crc(0, &untrusted_hdr, sizeof untrusted_hdr)) { while (read_all_with_crc(0, &untrusted_hdr, sizeof untrusted_hdr)) {
/* check for end of transfer marker */ /* check for end of transfer marker */
if (untrusted_hdr.namelen == 0) { if (untrusted_hdr.namelen == 0) {
errno = 0; errno = 0;
break; break;
} }
total_files++; total_files++;
if (files_limit && total_files > files_limit) if (files_limit && total_files > files_limit)
do_exit(EDQUOT, untrusted_namebuf); do_exit(EDQUOT, untrusted_namebuf);
process_one_file(&untrusted_hdr); process_one_file(&untrusted_hdr);
} }
#ifdef HAVE_SYNCFS #ifdef HAVE_SYNCFS
saved_errno = errno; saved_errno = errno;
cwd_fd = open(".", O_RDONLY); cwd_fd = open(".", O_RDONLY);
if (cwd_fd >= 0 && syncfs(cwd_fd) == 0 && close(cwd_fd) == 0) if (cwd_fd >= 0 && syncfs(cwd_fd) == 0 && close(cwd_fd) == 0)
errno = saved_errno; errno = saved_errno;
#endif #endif
send_status_and_crc(errno, untrusted_namebuf); send_status_and_crc(errno, untrusted_namebuf);
return errno; return errno;
} }

View File

@ -34,86 +34,86 @@ reports that "fd" is writable. Write as much as possible to fd.
*/ */
int flush_client_data(int fd, struct buffer *buffer) int flush_client_data(int fd, struct buffer *buffer)
{ {
int ret; int ret;
int len; int len;
for (;;) { for (;;) {
len = buffer_len(buffer); len = buffer_len(buffer);
if (!len) { if (!len) {
return WRITE_STDIN_OK; return WRITE_STDIN_OK;
} }
if (len > MAX_DATA_CHUNK) if (len > MAX_DATA_CHUNK)
len = MAX_DATA_CHUNK; len = MAX_DATA_CHUNK;
ret = write(fd, buffer_data(buffer), len); ret = write(fd, buffer_data(buffer), len);
if (ret == -1) { if (ret == -1) {
if (errno != EAGAIN) { if (errno != EAGAIN) {
return WRITE_STDIN_ERROR; return WRITE_STDIN_ERROR;
} else } else
return WRITE_STDIN_BUFFERED; return WRITE_STDIN_BUFFERED;
} }
// we previously called buffer_remove(buffer, len) // we previously called buffer_remove(buffer, len)
// it will be wrong if we change MAX_DATA_CHUNK to something large // it will be wrong if we change MAX_DATA_CHUNK to something large
// as pipes writes are atomic only to PIPE_MAX limit // as pipes writes are atomic only to PIPE_MAX limit
buffer_remove(buffer, ret); buffer_remove(buffer, ret);
} }
} }
/* /*
Write "len" bytes from "data" to "fd". If not all written, buffer the rest * Write "len" bytes from "data" to "fd". If not all written, buffer the rest
to "buffer". * to "buffer".
*/ */
int write_stdin(int fd, const char *data, int len, struct buffer *buffer) int write_stdin(int fd, const char *data, int len, struct buffer *buffer)
{ {
int ret; int ret;
int written = 0; int written = 0;
if (buffer_len(buffer)) { if (buffer_len(buffer)) {
buffer_append(buffer, data, len); buffer_append(buffer, data, len);
return WRITE_STDIN_BUFFERED; return WRITE_STDIN_BUFFERED;
} }
while (written < len) { while (written < len) {
ret = write(fd, data + written, len - written); ret = write(fd, data + written, len - written);
if (ret == 0) { if (ret == 0) {
perror("write_stdin: write returns 0 ???"); perror("write_stdin: write returns 0 ???");
exit(1); exit(1);
} }
if (ret == -1) { if (ret == -1) {
if (errno != EAGAIN) if (errno != EAGAIN)
return WRITE_STDIN_ERROR; return WRITE_STDIN_ERROR;
buffer_append(buffer, data + written, buffer_append(buffer, data + written,
len - written); len - written);
return WRITE_STDIN_BUFFERED; return WRITE_STDIN_BUFFERED;
} }
written += ret; written += ret;
} }
return WRITE_STDIN_OK; return WRITE_STDIN_OK;
} }
/* /*
Data feed process has exited, so we need to clear all control structures for * Data feed process has exited, so we need to clear all control structures for
the client. However, if we have buffered data for the client (which is rare btw), * the client. However, if we have buffered data for the client (which is rare btw),
fire&forget a separate process to flush them. * fire&forget a separate process to flush them.
*/ */
int fork_and_flush_stdin(int fd, struct buffer *buffer) int fork_and_flush_stdin(int fd, struct buffer *buffer)
{ {
int i; int i;
if (!buffer_len(buffer)) if (!buffer_len(buffer))
return 0; return 0;
switch (fork()) { switch (fork()) {
case -1: case -1:
perror("fork"); perror("fork");
exit(1); exit(1);
case 0: case 0:
break; break;
default: default:
return 1; return 1;
} }
for (i = 0; i < MAX_FDS; i++) for (i = 0; i < MAX_FDS; i++)
if (i != fd && i != 2) if (i != fd && i != 2)
close(i); close(i);
set_block(fd); set_block(fd);
write_all(fd, buffer_data(buffer), buffer_len(buffer)); write_all(fd, buffer_data(buffer), buffer_len(buffer));
_exit(0); _exit(0);
} }