implemented copyReg() with mmap(2) instead of read(2)+write(2) sequences
[util-vserver.git] / util-vserver / lib_internal / unify-copy.c
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              0x10000000
38 #define MMAP_BLOCKSIZE          0x4000
39
40 static inline bool
41 verifySource(int fd, struct stat const *exp_stat)
42 {
43   struct stat           st;
44   
45   return (fstat(fd, &st)!=-1 &&
46           st.st_dev==exp_stat->st_dev &&
47           st.st_ino==exp_stat->st_ino);
48 }
49
50 static inline bool
51 copyLnk(char const *src, char const *dst)
52 {
53   ssize_t       len = 1024;
54   for (;;) {
55     char        buf[len];
56     ssize_t     l;
57     l = readlink(src, buf, len-1);
58     if (l==-1) return false;
59     if (l>=len-1) {
60       len *= 2;
61       continue;
62     }
63     buf[l] = '\0';
64
65     return (symlink(buf, dst)!=-1);
66   }
67 }
68
69 static jmp_buf                  bus_error_restore;
70 static volatile sig_atomic_t    bus_error;
71
72 static void
73 handlerSIGBUS(int UNUSED num)
74 {
75   bus_error = 1;
76   longjmp(bus_error_restore, 1);
77 }
78
79 static UNUSED bool
80 copyMMap(int in_fd, int out_fd)
81 {
82   off_t                 in_len   = lseek(in_fd, 0, SEEK_END);
83   void volatile const   *in_buf  = 0;
84   void volatile         *out_buf = 0;
85   
86   volatile loff_t       in_size  = 0;
87   volatile loff_t       out_size = 0;
88   volatile bool         res      = false;
89
90   if (in_len==-1) return false;
91   if (in_len>0 &&
92       (lseek(out_fd, in_len-1, SEEK_SET)==-1 ||
93        write(out_fd, "\0",     1)!=1))          // create sparse file
94     return false;
95   
96   bus_error = 0;
97   if (setjmp(bus_error_restore)==0) {
98     off_t               offset = 0;
99
100     while (offset < in_len) {
101       in_size = in_len - offset;
102       if (in_size > MMAP_BLOCKSIZE) in_size = MMAP_BLOCKSIZE;
103       
104       in_buf   = mmap(0, in_size,  PROT_READ,  MAP_SHARED, in_fd,  offset);
105       if (in_buf==0)  goto out;
106
107       out_size = in_size;
108       out_buf  = mmap(0, out_size, PROT_WRITE, MAP_SHARED, out_fd, offset);
109       if (out_buf==0) goto out;
110
111       offset  += in_size;
112       madvise(const_cast(void *)(in_buf),  in_size,  MADV_SEQUENTIAL);
113       madvise(out_buf,                     out_size, MADV_SEQUENTIAL);
114
115       memcpy(out_buf, in_buf, in_size);
116
117       munmap(const_cast(void *)(in_buf),  in_size);  in_buf  = 0;
118       munmap(out_buf,                     out_size); out_buf = 0;
119     }
120
121     res = true;
122   }
123
124   out:
125   if (in_buf!=0)  munmap(const_cast(void *)(in_buf),  in_size);
126   if (out_buf!=0) munmap(out_buf,                     out_size);
127
128   return res;
129 }
130
131 static inline bool
132 copyReg(char const *src, struct stat const *src_stat,
133         char const *dst)
134 {
135   int           in_fd  = open(src, O_RDONLY|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW|O_LARGEFILE);
136   int           out_fd = in_fd==-1 ? -1 : open(dst, O_RDWR|O_CREAT|O_EXCL, 0200);
137   bool          res    = false;
138   
139   if (in_fd==-1 || out_fd==-1 ||
140       !verifySource(in_fd, src_stat)) goto err;
141
142 #if 0  
143   for (;;) {
144     char        buf[2048];
145     ssize_t     l = read(in_fd, buf, sizeof buf);
146     if (l==-1) goto err;
147     if (l==0)  break;
148     if (!WwriteAll(out_fd, buf, l, 0)) goto err;
149   }
150
151   res = true;
152 #else
153   void          (*old_handler)(int) = signal(SIGBUS, handlerSIGBUS);
154
155   res = copyMMap(in_fd, out_fd);
156
157   signal(SIGBUS, old_handler);
158 #endif
159
160   err:
161   if (out_fd!=-1 && close(out_fd)==-1) res=false;
162   if (in_fd!=-1  && close(in_fd)==-1)  res=false;
163   return res;
164 }
165
166 static inline bool
167 copyNode(char const UNUSED *src, struct stat const *src_stat,
168          char const *dst)
169 {
170   return mknod(dst, src_stat->st_mode & (S_IFMT|S_IWUSR),
171                src_stat->st_rdev)!=-1;
172 }
173
174 static inline bool
175 copyDir(char const UNUSED *src, struct stat const UNUSED *src_stat,
176         char const *dst)
177 {
178   return mkdir(dst, 0700)!=-1;
179 }
180
181 static inline bool
182 setModes(char const *path, struct stat const *st)
183 {
184   return (lchown(path, st->st_uid, st->st_gid)!=-1 &&
185           (S_ISLNK(st->st_mode) || chmod(path, st->st_mode)!=-1));
186 }
187
188
189 bool
190 Unify_copy(char const *src, struct stat const *src_stat,
191            char const *dst)
192 {
193   // skip sockets
194   // TODO: message
195   if (S_ISSOCK(src_stat->st_mode))
196     return true;
197   
198   return
199     (((S_ISLNK (src_stat->st_mode) && copyLnk (src, dst)) ||
200       (S_ISREG (src_stat->st_mode) && copyReg (src, src_stat, dst)) ||
201       (S_ISDIR (src_stat->st_mode) && copyDir (src, src_stat, dst)) ||
202       ((S_ISBLK (src_stat->st_mode) ||
203         S_ISCHR (src_stat->st_mode) || 
204         S_ISFIFO(src_stat->st_mode)) && copyNode(src, src_stat, dst))
205       ) &&
206      setModes(dst, src_stat) &&
207      Unify_setTime(dst, src_stat));
208 }