X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fshared%2Ffileio.c;h=ede88196b5f1a9374a5b387a9d72278bf86255cb;hb=0371ca0dac1d70b2e5060a3c4e6fbbc2bdbd8671;hp=4e2b4442db1fb8756ff52cd8cdbf0e2098da848d;hpb=69ab80881552d5f79ca95f6b3be48ad122ab1ec2;p=elogind.git diff --git a/src/shared/fileio.c b/src/shared/fileio.c index 4e2b4442d..ede88196b 100644 --- a/src/shared/fileio.c +++ b/src/shared/fileio.c @@ -20,10 +20,12 @@ ***/ #include +#include #include "fileio.h" #include "util.h" #include "strv.h" #include "utf8.h" +#include "ctype.h" int write_string_to_file(FILE *f, const char *line) { errno = 0; @@ -116,6 +118,77 @@ int read_one_line_file(const char *fn, char **line) { return 0; } +ssize_t sendfile_full(int out_fd, const char *fn) { + _cleanup_fclose_ FILE *f; + struct stat st; + int r; + ssize_t s; + + size_t n, l; + _cleanup_free_ char *buf = NULL; + + assert(out_fd > 0); + assert(fn); + + f = fopen(fn, "r"); + if (!f) + return -errno; + + r = fstat(fileno(f), &st); + if (r < 0) + return -errno; + + s = sendfile(out_fd, fileno(f), NULL, st.st_size); + if (s < 0) + if (errno == EINVAL || errno == ENOSYS) { + /* continue below */ + } else + return -errno; + else + return s; + + /* sendfile() failed, fall back to read/write */ + + /* Safety check */ + if (st.st_size > 4*1024*1024) + return -E2BIG; + + n = st.st_size > 0 ? st.st_size : LINE_MAX; + l = 0; + + while (true) { + char *t; + size_t k; + + t = realloc(buf, n); + if (!t) + return -ENOMEM; + + buf = t; + k = fread(buf + l, 1, n - l, f); + + if (k <= 0) { + if (ferror(f)) + return -errno; + + break; + } + + l += k; + n *= 2; + + /* Safety check */ + if (n > 4*1024*1024) + return -E2BIG; + } + + r = write(out_fd, buf, l); + if (r < 0) + return -errno; + + return (ssize_t) l; +} + int read_full_file(const char *fn, char **contents, size_t *size) { _cleanup_fclose_ FILE *f = NULL; size_t n, l; @@ -167,7 +240,7 @@ int read_full_file(const char *fn, char **contents, size_t *size) { buf[l] = 0; *contents = buf; - buf = NULL; + buf = NULL; /* do not free */ if (size) *size = l; @@ -650,9 +723,9 @@ int executable_is_script(const char *path, char **interpreter) { } /** - * Retrieve one field from a file like /proc/self/status. - * pattern should start with '\n' and end with ':'. Whitespace - * after ':' will be skipped. field must be freed afterwards. + * Retrieve one field from a file like /proc/self/status. pattern + * should start with '\n' and end with a ':'. Whitespace and zeros + * after the ':' will be skipped. field must be freed afterwards. */ int get_status_field(const char *filename, const char *pattern, char **field) { _cleanup_free_ char *status = NULL; @@ -661,6 +734,7 @@ int get_status_field(const char *filename, const char *pattern, char **field) { int r; assert(filename); + assert(pattern); assert(field); r = read_full_file(filename, &status, NULL); @@ -672,7 +746,20 @@ int get_status_field(const char *filename, const char *pattern, char **field) { return -ENOENT; t += strlen(pattern); - t += strspn(t, WHITESPACE); + if (*t) { + t += strspn(t, " \t"); + + /* Also skip zeros, because when this is used for + * capabilities, we don't want the zeros. This way the + * same capability set always maps to the same string, + * irrespective of the total capability set size. For + * other numbers it shouldn't matter. */ + t += strspn(t, "0"); + /* Back off one char if there's nothing but whitespace + and zeros */ + if (!*t || isspace(*t)) + t --; + } len = strcspn(t, WHITESPACE);