Really don't rely on util-python anymore.
[util-vserver.git] / lib_internal / unify-copy.c
1 // $Id: unify-copy.c 2485 2007-02-04 17:18:27Z ensc $    --*- 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))==0 ||
136           (out_buf = mmap(0, buf_size, PROT_WRITE, MAP_SHARED, out_fd, offset))==0) {
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(const_cast(void *)(in_buf),  buf_size);  in_buf = 0;
149       munmap(out_buf,                     buf_size); out_buf = 0;
150     }
151
152     res = true;
153   }
154
155   out:
156   if (in_buf !=0) munmap(const_cast(void *)(in_buf),  buf_size);
157   if (out_buf!=0) munmap(out_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 }