X-Git-Url: http://git.linux-vserver.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=util-vserver%2Flib_internal%2Funify-unify.c;h=8a8e1d5530cd273591eb006c4b6935258491feae;hb=59824e5c9b22dd9c007ce8bf4ea5acb755a50dee;hp=6423f5888ab0d4bb6773c506934e0701b6e83e2b;hpb=5269e9607d89ac4dbd34f9adb5b14fb5dfc9b1a5;p=util-vserver.git diff --git a/util-vserver/lib_internal/unify-unify.c b/util-vserver/lib_internal/unify-unify.c index 6423f58..8a8e1d5 100644 --- a/util-vserver/lib_internal/unify-unify.c +++ b/util-vserver/lib_internal/unify-unify.c @@ -26,39 +26,70 @@ #include #include #include +#include #include +#include #include bool -Unify_unify(char const *src, struct stat const *src_stat, - char const *dst, struct stat const UNUSED *dst_stat) +Unify_unify(char const *src, struct stat const UNUSED *src_stat, + char const *dst, bool ignore_zero) { size_t l = strlen(dst); char tmpfile[l + sizeof(";XXXXXX")]; int fd; bool res = false; + struct stat st; + bool lstat_succeeded; + sigset_t mask_new, mask_old; + int old_errno; // at first, set the ILI flags on 'src' - if (vc_set_iattr_compat(src, src_stat->st_dev, src_stat->st_ino, - 0, VC_IATTR_IUNLINK, VC_IATTR_IUNLINK, - &src_stat->st_mode)==-1) + if (vc_set_iattr(src, + 0, + VC_IATTR_IUNLINK|VC_IATTR_IMMUTABLE, + VC_IATTR_IUNLINK|VC_IATTR_IMMUTABLE)==-1) { + perror("vc_set_iattr()"); return false; + } - // now, create a temporary filename - memcpy(tmpfile, dst, l); - memcpy(tmpfile+l, ";XXXXXX", 8); - fd = mkstemp(tmpfile); - close(fd); + lstat_succeeded = lstat(dst, &st)==0; - if (fd==-1) { - perror("mkstemp()"); + sigfillset(&mask_new); + if (sigprocmask(SIG_SETMASK, &mask_new, &mask_old)==-1) { + perror("sigprocmask()"); return false; } + + + // check if 'dst' already exists + // when ignore_zero is true, do not make backups of empty destinations + if (lstat_succeeded && (st.st_size>0 || !ignore_zero)) { + // now, create a temporary filename + memcpy(tmpfile, dst, l); + memcpy(tmpfile+l, ";XXXXXX", 8); + fd = mkstemp(tmpfile); + close(fd); - // and rename the old file to this name - if (rename(dst, tmpfile)==-1) { - perror("rename()"); - goto err; + if (fd==-1) { + perror("mkstemp()"); + tmpfile[0] = '\0'; + goto err; + } + + // and rename the old file to this name + + // NOTE: this rename() is race-free; when an attacker makes 'tmpfile' a + // directory, the operation would fail; when making it a symlink to a file + // or directory, the symlink but not the file/directory would be overridden + if (rename(dst, tmpfile)==-1) { + perror("rename()"); + goto err; + } + } + else { + if (lstat_succeeded) unlink(dst); + tmpfile[0] = '\0'; } // now, link the src-file to dst @@ -66,7 +97,8 @@ Unify_unify(char const *src, struct stat const *src_stat, perror("link()"); unlink(dst); - if (rename(tmpfile, dst)==-1) { + if (tmpfile[0]!='\0' && + rename(tmpfile, dst)==-1) { perror("FATAL error in rename()"); _exit(1); } @@ -76,7 +108,14 @@ Unify_unify(char const *src, struct stat const *src_stat, res = true; err: - unlink(tmpfile); + old_errno = errno; + + if (tmpfile[0]!='\0') + unlink(tmpfile); + + if (sigprocmask(SIG_SETMASK, &mask_old, 0)==-1) + perror("sigprocmask()"); + errno = old_errno; return res; }