util: make a couple of files we write atomic

This commit is contained in:
Lennart Poettering 2011-06-15 15:35:23 +02:00
parent 5f4b19f4bc
commit 34ca941cec
5 changed files with 260 additions and 22 deletions

View file

@ -201,7 +201,7 @@ static int write_data_static_hostname(void) {
return 0;
}
return write_one_line_file("/etc/hostname", data[PROP_STATIC_HOSTNAME]);
return write_one_line_file_atomic("/etc/hostname", data[PROP_STATIC_HOSTNAME]);
}
static int write_data_other(void) {

View file

@ -320,7 +320,7 @@ int main(int argc, char *argv[]) {
log_info("Creating /run/nologin, blocking further logins...");
if ((e = write_one_line_file("/run/nologin", "System is going down.")) < 0)
if ((e = write_one_line_file_atomic("/run/nologin", "System is going down.")) < 0)
log_error("Failed to create /run/nologin: %s", strerror(-e));
else
unlink_nologin = true;

View file

@ -67,7 +67,7 @@ int main(int argc, char*argv[]) {
int r, q;
char *cgroup_user_tree = NULL;
if ((r = write_one_line_file("/run/nologin", "System is going down.")) < 0)
if ((r = write_one_line_file_atomic("/run/nologin", "System is going down.")) < 0)
log_error("Failed to create /run/nologin: %s", strerror(-r));
if ((q = cg_get_user_path(&cgroup_user_tree)) < 0) {

View file

@ -567,6 +567,7 @@ int write_one_line_file(const char *fn, const char *line) {
if (!(f = fopen(fn, "we")))
return -errno;
errno = 0;
if (fputs(line, f) < 0) {
r = -errno;
goto finish;
@ -590,6 +591,64 @@ finish:
return r;
}
int fchmod_umask(int fd, mode_t m) {
mode_t u;
int r;
u = umask(0777);
r = fchmod(fd, m & (~u)) < 0 ? -errno : 0;
umask(u);
return r;
}
int write_one_line_file_atomic(const char *fn, const char *line) {
FILE *f;
int r;
char *p;
assert(fn);
assert(line);
r = fopen_temporary(fn, &f, &p);
if (r < 0)
return r;
fchmod_umask(fileno(f), 0644);
errno = 0;
if (fputs(line, f) < 0) {
r = -errno;
goto finish;
}
if (!endswith(line, "\n"))
fputc('\n', f);
fflush(f);
if (ferror(f)) {
if (errno != 0)
r = -errno;
else
r = -EIO;
} else {
if (rename(p, fn) < 0)
r = -errno;
else
r = 0;
}
finish:
if (r < 0)
unlink(p);
fclose(f);
free(p);
return r;
}
int read_one_line_file(const char *fn, char **line) {
FILE *f;
int r;
@ -621,7 +680,7 @@ finish:
return r;
}
int read_full_file(const char *fn, char **contents) {
int read_full_file(const char *fn, char **contents, size_t *size) {
FILE *f;
int r;
size_t n, l;
@ -636,6 +695,12 @@ int read_full_file(const char *fn, char **contents) {
goto finish;
}
/* Safety check */
if (st.st_size > 4*1024*1024) {
r = -E2BIG;
goto finish;
}
n = st.st_size > 0 ? st.st_size : LINE_MAX;
l = 0;
@ -680,6 +745,9 @@ int read_full_file(const char *fn, char **contents) {
*contents = buf;
buf = NULL;
if (size)
*size = l;
r = 0;
finish:
@ -699,7 +767,7 @@ int parse_env_file(
assert(fname);
assert(separator);
if ((r = read_full_file(fname, &contents)) < 0)
if ((r = read_full_file(fname, &contents, NULL)) < 0)
return r;
p = contents;
@ -838,15 +906,17 @@ finish:
}
int write_env_file(const char *fname, char **l) {
char **i;
char **i, *p;
FILE *f;
int r;
f = fopen(fname, "we");
if (!f)
return -errno;
r = fopen_temporary(fname, &f, &p);
if (r < 0)
return r;
fchmod_umask(fileno(f), 0644);
errno = 0;
STRV_FOREACH(i, l) {
fputs(*i, f);
fputc('\n', f);
@ -854,8 +924,23 @@ int write_env_file(const char *fname, char **l) {
fflush(f);
r = ferror(f) ? -errno : 0;
if (ferror(f)) {
if (errno != 0)
r = -errno;
else
r = -EIO;
} else {
if (rename(p, fname) < 0)
r = -errno;
else
r = 0;
}
if (r < 0)
unlink(p);
fclose(f);
free(p);
return r;
}
@ -4778,6 +4863,152 @@ int hwclock_set_time(const struct tm *tm) {
return err;
}
int copy_file(const char *from, const char *to) {
int r, fdf, fdt;
assert(from);
assert(to);
fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fdf < 0)
return -errno;
fdt = open(to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY, 0644);
if (fdt < 0) {
close_nointr_nofail(fdf);
return -errno;
}
for (;;) {
char buf[PIPE_BUF];
ssize_t n, k;
n = read(fdf, buf, sizeof(buf));
if (n < 0) {
r = -errno;
close_nointr_nofail(fdf);
close_nointr(fdt);
unlink(to);
return r;
}
if (n == 0)
break;
errno = 0;
k = loop_write(fdt, buf, n, false);
if (n != k) {
r = k < 0 ? k : (errno ? -errno : -EIO);
close_nointr_nofail(fdf);
close_nointr(fdt);
unlink(to);
return r;
}
}
close_nointr_nofail(fdf);
r = close_nointr(fdt);
if (r < 0) {
unlink(to);
return r;
}
return 0;
}
int symlink_or_copy(const char *from, const char *to) {
char *pf = NULL, *pt = NULL;
struct stat a, b;
int r;
assert(from);
assert(to);
if (parent_of_path(from, &pf) < 0 ||
parent_of_path(to, &pt) < 0) {
r = -ENOMEM;
goto finish;
}
if (stat(pf, &a) < 0 ||
stat(pt, &b) < 0) {
r = -errno;
goto finish;
}
if (a.st_dev != b.st_dev) {
free(pf);
free(pt);
return copy_file(from, to);
}
if (symlink(from, to) < 0) {
r = -errno;
goto finish;
}
r = 0;
finish:
free(pf);
free(pt);
return r;
}
int symlink_or_copy_atomic(const char *from, const char *to) {
char *t, *x;
const char *fn;
size_t k;
unsigned long long ull;
unsigned i;
int r;
assert(from);
assert(to);
t = new(char, strlen(to) + 1 + 16 + 1);
if (!t)
return -ENOMEM;
fn = file_name_from_path(to);
k = fn-to;
memcpy(t, to, k);
t[k] = '.';
x = stpcpy(t+k+1, fn);
ull = random_ull();
for (i = 0; i < 16; i++) {
*(x++) = hexchar(ull & 0xF);
ull >>= 4;
}
*x = 0;
r = symlink_or_copy(from, t);
if (r < 0) {
unlink(t);
free(t);
return r;
}
if (rename(t, to) < 0) {
r = -errno;
unlink(t);
free(t);
return r;
}
free(t);
return r;
}
static const char *const ioprio_class_table[] = {
[IOPRIO_CLASS_NONE] = "none",
[IOPRIO_CLASS_RT] = "realtime",

View file

@ -200,8 +200,9 @@ pid_t get_parent_of_pid(pid_t pid, pid_t *ppid);
int get_starttime_of_pid(pid_t pid, unsigned long long *st);
int write_one_line_file(const char *fn, const char *line);
int write_one_line_file_atomic(const char *fn, const char *line);
int read_one_line_file(const char *fn, char **line);
int read_full_file(const char *fn, char **contents);
int read_full_file(const char *fn, char **contents, size_t *size);
int parse_env_file(const char *fname, const char *separator, ...) _sentinel_;
int load_env_file(const char *fname, char ***l);
@ -422,6 +423,22 @@ int terminal_vhangup(const char *name);
int vt_disallocate(const char *name);
int copy_file(const char *from, const char *to);
int symlink_or_copy(const char *from, const char *to);
int symlink_or_copy_atomic(const char *from, const char *to);
int fchmod_umask(int fd, mode_t mode);
int conf_files_list(char ***strv, const char *suffix, const char *dir, ...);
bool hwclock_is_localtime(void);
int hwclock_apply_localtime_delta(void);
int hwclock_get_time(struct tm *tm);
int hwclock_set_time(const struct tm *tm);
#define NULSTR_FOREACH(i, l) \
for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)
@ -454,14 +471,4 @@ int signal_from_string(const char *s);
int signal_from_string_try_harder(const char *s);
int conf_files_list(char ***strv, const char *suffix, const char *dir, ...);
bool hwclock_is_localtime(void);
int hwclock_apply_localtime_delta(void);
int hwclock_get_time(struct tm *tm);
int hwclock_set_time(const struct tm *tm);
#endif