#include <stdlib.h>
#include <string.h>
#include <stdio.h>
+#include <signal.h>
#include <unistd.h>
+#include <errno.h>
#include <sys/stat.h>
bool
Unify_unify(char const *src, struct stat const UNUSED *src_stat,
- char const *dst)
+ 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(src,
0,
VC_IATTR_IUNLINK|VC_IATTR_IMMUTABLE,
- VC_IATTR_IUNLINK|VC_IATTR_IMMUTABLE)==-1)
+ 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
perror("link()");
unlink(dst);
- if (rename(tmpfile, dst)==-1) {
+ if (tmpfile[0]!='\0' &&
+ rename(tmpfile, dst)==-1) {
perror("FATAL error in rename()");
_exit(1);
}
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;
}