unify-unify.c (3035B)
1 // $Id$ --*- c -*-- 2 3 // Copyright (C) 2004 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de> 4 // 5 // This program is free software; you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation; version 2 of the License. 8 // 9 // This program is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with this program; if not, write to the Free Software 16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 18 19 #ifdef HAVE_CONFIG_H 20 # include <config.h> 21 #endif 22 23 #include "unify.h" 24 #include "vserver.h" 25 26 #include <stdlib.h> 27 #include <string.h> 28 #include <stdio.h> 29 #include <signal.h> 30 #include <unistd.h> 31 #include <errno.h> 32 #include <sys/stat.h> 33 34 bool 35 Unify_unify(char const *src, struct stat const UNUSED *src_stat, 36 char const *dst, bool ignore_zero) 37 { 38 size_t l = strlen(dst); 39 char tmpfile[l + sizeof(";XXXXXX")]; 40 int fd; 41 bool res = false; 42 struct stat st; 43 bool lstat_succeeded; 44 sigset_t mask_new, mask_old; 45 int old_errno; 46 47 // at first, set the ILI flags on 'src' 48 if (vc_set_iattr(src, 49 0, 50 VC_IATTR_IUNLINK|VC_IATTR_IMMUTABLE, 51 VC_IATTR_IUNLINK|VC_IATTR_IMMUTABLE)==-1) { 52 perror("vc_set_iattr()"); 53 return false; 54 } 55 56 lstat_succeeded = lstat(dst, &st)==0; 57 58 sigfillset(&mask_new); 59 if (sigprocmask(SIG_SETMASK, &mask_new, &mask_old)==-1) { 60 perror("sigprocmask()"); 61 return false; 62 } 63 64 65 // check if 'dst' already exists 66 // when ignore_zero is true, do not make backups of empty destinations 67 if (lstat_succeeded && (st.st_size>0 || !ignore_zero)) { 68 // now, create a temporary filename 69 memcpy(tmpfile, dst, l); 70 memcpy(tmpfile+l, ";XXXXXX", 8); 71 fd = mkstemp(tmpfile); 72 close(fd); 73 74 if (fd==-1) { 75 perror("mkstemp()"); 76 tmpfile[0] = '\0'; 77 goto err; 78 } 79 80 // and rename the old file to this name 81 82 // NOTE: this rename() is race-free; when an attacker makes 'tmpfile' a 83 // directory, the operation would fail; when making it a symlink to a file 84 // or directory, the symlink but not the file/directory would be overridden 85 if (rename(dst, tmpfile)==-1) { 86 perror("rename()"); 87 goto err; 88 } 89 } 90 else { 91 if (lstat_succeeded) unlink(dst); 92 tmpfile[0] = '\0'; 93 } 94 95 // now, link the src-file to dst 96 if (link(src, dst)==-1) { 97 perror("link()"); 98 99 unlink(dst); 100 if (tmpfile[0]!='\0' && 101 rename(tmpfile, dst)==-1) { 102 perror("FATAL error in rename()"); 103 _exit(1); 104 } 105 goto err; 106 } 107 108 res = true; 109 110 err: 111 old_errno = errno; 112 113 if (tmpfile[0]!='\0') 114 unlink(tmpfile); 115 116 if (sigprocmask(SIG_SETMASK, &mask_old, 0)==-1) 117 perror("sigprocmask()"); 118 119 errno = old_errno; 120 return res; 121 }