unify-copy.c (5820B)
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 "util.h" 25 26 #include <unistd.h> 27 #include <errno.h> 28 #include <fcntl.h> 29 #include <setjmp.h> 30 #include <signal.h> 31 #include <sys/stat.h> 32 #include <sys/mman.h> 33 34 #define ENSC_WRAPPERS_IO 1 35 #include <wrappers.h> 36 37 #define MMAP_BLOCKSIZE (16 * 1024*1024) 38 39 #ifndef TESTSUITE_COPY_CODE 40 # define TESTSUITE_COPY_CODE do { } while (false) 41 #endif 42 43 static inline bool 44 verifySource(int fd, struct stat const *exp_stat) 45 { 46 struct stat st; 47 48 return (fstat(fd, &st)!=-1 && 49 st.st_dev==exp_stat->st_dev && 50 st.st_ino==exp_stat->st_ino); 51 } 52 53 static inline bool 54 copyLnk(char const *src, char const *dst) 55 { 56 ssize_t len = 1024; 57 for (;;) { 58 char buf[len]; 59 ssize_t l; 60 l = readlink(src, buf, len-1); 61 if (l==-1) return false; 62 if (l>=len-1) { 63 len *= 2; 64 continue; 65 } 66 buf[l] = '\0'; 67 68 return (symlink(buf, dst)!=-1); 69 } 70 } 71 72 static sigjmp_buf bus_error_restore; 73 static volatile sig_atomic_t bus_error; 74 75 static void 76 handlerSIGBUS(int UNUSED num) 77 { 78 bus_error = 1; 79 siglongjmp(bus_error_restore, 1); 80 } 81 82 static void 83 copyMem(void *dst_v, void const *src_v, size_t len_v) 84 { 85 #if 1 86 // Do not use memcpy because this would dirty pages consisting only of 87 // '\0' 88 int *dst = dst_v; 89 int const *src = src_v; 90 size_t len = len_v / sizeof(int); 91 size_t rest = len_v - sizeof(int)*len; 92 size_t i=0; 93 94 for (; i<len; ++i) { 95 if (*src != 0) *dst = *src; 96 ++dst; 97 ++src; 98 } 99 100 char *dst_c = (void *)(dst); 101 char const *src_c = (void const *)(src); 102 103 for (i=0; i<rest; ++i) { 104 if (*src_c != 0) *dst_c = *src_c; 105 ++dst_c; 106 ++src_c; 107 } 108 #else 109 memcpy(dst_v, src_v, len_v); 110 #endif 111 } 112 113 static UNUSED bool 114 copyMMap(int in_fd, int out_fd) 115 { 116 off_t in_len = lseek(in_fd, 0, SEEK_END); 117 void const * volatile in_buf = 0; 118 void * volatile out_buf = 0; 119 120 loff_t volatile buf_size = 0; 121 bool volatile res = false; 122 123 if (in_len==-1) return false; 124 if (in_len>0 && ftruncate(out_fd, in_len)==-1) // create sparse file 125 return false; 126 127 bus_error = 0; 128 if (sigsetjmp(bus_error_restore, 1)==0) { 129 off_t offset = 0; 130 131 while (offset < in_len) { 132 buf_size = in_len - offset; 133 if (buf_size > MMAP_BLOCKSIZE) buf_size = MMAP_BLOCKSIZE; 134 135 if ((in_buf = mmap(0, buf_size, PROT_READ, MAP_SHARED, in_fd, offset))==MAP_FAILED || 136 (out_buf = mmap(0, buf_size, PROT_WRITE, MAP_SHARED, out_fd, offset))==MAP_FAILED) { 137 perror("mmap()"); 138 goto out; 139 } 140 141 offset += buf_size; 142 madvise(const_cast(void *)(in_buf), buf_size, MADV_SEQUENTIAL); 143 madvise(out_buf, buf_size, MADV_SEQUENTIAL); 144 145 TESTSUITE_COPY_CODE; 146 copyMem(out_buf, in_buf, buf_size); 147 148 munmap(out_buf, buf_size); out_buf = 0; 149 munmap(const_cast(void *)(in_buf), buf_size); in_buf = 0; 150 } 151 152 res = true; 153 } 154 155 out: 156 if (out_buf!=0) munmap(out_buf, buf_size); 157 if (in_buf !=0) munmap(const_cast(void *)(in_buf), buf_size); 158 159 return res; 160 } 161 162 static inline bool 163 copyReg(char const *src, struct stat const *src_stat, 164 char const *dst) 165 { 166 int in_fd = open(src, O_RDONLY|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW|O_LARGEFILE); 167 int out_fd = in_fd==-1 ? -1 : open(dst, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY, 0200); 168 bool res = false; 169 170 if (in_fd==-1 || out_fd==-1 || 171 !verifySource(in_fd, src_stat)) goto err; 172 173 #if 0 174 for (;;) { 175 char buf[2048]; 176 ssize_t l = read(in_fd, buf, sizeof buf); 177 if (l==-1) goto err; 178 if (l==0) break; 179 if (!WwriteAll(out_fd, buf, l, 0)) goto err; 180 } 181 182 res = true; 183 #else 184 void (*old_handler)(int) = signal(SIGBUS, handlerSIGBUS); 185 186 res = copyMMap(in_fd, out_fd); 187 188 signal(SIGBUS, old_handler); 189 #endif 190 191 err: 192 if (out_fd!=-1 && close(out_fd)==-1) res=false; 193 if (in_fd!=-1 && close(in_fd)==-1) res=false; 194 return res; 195 } 196 197 static inline bool 198 copyNode(char const UNUSED *src, struct stat const *src_stat, 199 char const *dst) 200 { 201 return mknod(dst, src_stat->st_mode & (S_IFMT|S_IWUSR), 202 src_stat->st_rdev)!=-1; 203 } 204 205 static inline bool 206 copyDir(char const UNUSED *src, struct stat const UNUSED *src_stat, 207 char const *dst) 208 { 209 return mkdir(dst, 0700)!=-1; 210 } 211 212 static inline bool 213 setModes(char const *path, struct stat const *st) 214 { 215 return (lchown(path, st->st_uid, st->st_gid)!=-1 && 216 (S_ISLNK(st->st_mode) || chmod(path, st->st_mode)!=-1)); 217 } 218 219 220 bool 221 Unify_copy(char const *src, struct stat const *src_stat, 222 char const *dst) 223 { 224 // skip sockets 225 // TODO: message 226 if (S_ISSOCK(src_stat->st_mode)) 227 return true; 228 229 return 230 (((S_ISLNK (src_stat->st_mode) && copyLnk (src, dst)) || 231 (S_ISREG (src_stat->st_mode) && copyReg (src, src_stat, dst)) || 232 (S_ISDIR (src_stat->st_mode) && copyDir (src, src_stat, dst)) || 233 ((S_ISBLK (src_stat->st_mode) || 234 S_ISCHR (src_stat->st_mode) || 235 S_ISFIFO(src_stat->st_mode)) && copyNode(src, src_stat, dst)) 236 ) && 237 setModes(dst, src_stat) && 238 Unify_setTime(dst, src_stat)); 239 }