xref: /dragonfly/sbin/newfs/fscopy.c (revision 9bb2a92d)
1 /*
2  * Copyright (c) 2003 Matthew Dillon <dillon@backplane.com>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $DragonFly: src/sbin/newfs/fscopy.c,v 1.2 2004/02/04 17:40:00 joerg Exp $
27  */
28 
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <stdio.h>
32 #include <stdarg.h>
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <dirent.h>
36 #include <errno.h>
37 #ifdef DIRBLKSIZ
38 #undef DIRBLKSIZ
39 #endif
40 #include "defs.h"
41 
42 struct FSNode {
43     struct FSNode	*fs_Next;
44     struct FSNode	*fs_HNext;
45     struct FSNode	*fs_Base;
46     struct FSNode	*fs_Parent;
47     struct stat		fs_St;
48     char		*fs_Data;
49     int			fs_Bytes;
50     int			fs_Marker;
51     char		fs_Name[4];
52 };
53 
54 static
55 fsnode_t
56 fsmknode(const char *path)
57 {
58     int pathlen = strlen(path);
59     fsnode_t node = malloc(offsetof(struct FSNode, fs_Name[pathlen+1]));
60 
61     if (node == NULL) {
62 	fprintf(stderr, "ran out of memory copying filesystem\n");
63 	return(NULL);
64     }
65     bzero(node, sizeof(*node));
66     bcopy(path, node->fs_Name, pathlen + 1);
67     if (lstat(path, &node->fs_St) < 0) {
68 	fprintf(stderr, "Unable to lstat(\"%s\")\n", path);
69 	free(node);
70 	return(NULL);
71     }
72     return(node);
73 }
74 
75 fsnode_t
76 fsgethlink(fsnode_t hlinks, fsnode_t node)
77 {
78     fsnode_t scan;
79 
80     for (scan = hlinks; scan; scan = scan->fs_HNext) {
81 	if (scan->fs_St.st_dev == node->fs_St.st_dev &&
82 	    scan->fs_St.st_ino == node->fs_St.st_ino
83 	) {
84 	    return(scan);
85 	}
86     }
87     return(NULL);
88 }
89 
90 char *
91 fshardpath(fsnode_t hlink, fsnode_t node)
92 {
93     fsnode_t scan;
94     char *path;
95     char *tmp;
96 
97     for (scan = hlink; scan; scan = scan->fs_Parent)
98 	scan->fs_Marker = 1;
99     for (scan = node; scan; scan = scan->fs_Parent) {
100 	if (scan->fs_Marker == 1) {
101 	    scan->fs_Marker = 2;
102 	    break;
103 	}
104     }
105     if (scan == NULL)
106 	return(NULL);
107 
108     /*
109      * Build the path backwards
110      */
111     asprintf(&path, "%s", hlink->fs_Name);
112     for (scan = hlink->fs_Parent; scan->fs_Marker == 1; scan = scan->fs_Parent) {
113 	tmp = path;
114 	asprintf(&path, "%s/%s", scan->fs_Name, tmp);
115 	free(tmp);
116     }
117     for (scan = node->fs_Parent; scan; scan = scan->fs_Parent) {
118 	if (scan->fs_Marker == 2)
119 	    break;
120 	tmp = path;
121 	asprintf(&path, "../%s", tmp);
122 	free(tmp);
123     }
124 
125     for (scan = hlink; scan; scan = scan->fs_Parent)
126 	scan->fs_Marker = 0;
127     for (scan = node; scan; scan = scan->fs_Parent)
128 	scan->fs_Marker = 0;
129     return(path);
130 }
131 
132 fsnode_t
133 FSCopy(fsnode_t *phlinks, const char *path)
134 {
135     int n;
136     DIR *dir;
137     fsnode_t node;
138     char buf[1024];
139 
140     node = fsmknode(path);
141     if (node) {
142 	switch(node->fs_St.st_mode & S_IFMT) {
143 	case S_IFIFO:
144 	    break;
145 	case S_IFCHR:
146 	    break;
147 	case S_IFDIR:
148 	    if ((dir = opendir(path)) != NULL) {
149 		struct dirent *den;
150 		fsnode_t scan;
151 		fsnode_t *pscan;
152 
153 		if (chdir(path) < 0) {
154 		    fprintf(stderr, "Unable to chdir into %s\n", path);
155 		    break;
156 		}
157 		pscan = &node->fs_Base;
158 		while ((den = readdir(dir)) != NULL) {
159 		    if (den->d_namlen == 1 && den->d_name[0] == '.')
160 			continue;
161 		    if (den->d_namlen == 2 && den->d_name[0] == '.' &&
162 			den->d_name[1] == '.'
163 		    ) {
164 			continue;
165 		    }
166 		    scan = FSCopy(phlinks, den->d_name);
167 		    if (scan) {
168 			*pscan = scan;
169 			scan->fs_Parent = node;
170 			pscan = &scan->fs_Next;
171 		    }
172 		}
173 		if (chdir("..") < 0) {
174 		    fprintf(stderr, "Unable to chdir .. after scanning %s\n", path);
175 		    exit(1);
176 		}
177 		closedir(dir);
178 	    }
179 	    break;
180 	case S_IFBLK:
181 	    break;
182 	case S_IFREG:
183 	    if (node->fs_St.st_nlink > 1 && fsgethlink(*phlinks, node)) {
184 		node->fs_Bytes = -1;	/* hardlink indicator */
185 	    } else if (node->fs_St.st_size >= 0x80000000LL) {
186 		fprintf(stderr, "File %s too large to copy\n", path);
187 		free(node);
188 		node = NULL;
189 	    } else if ((node->fs_Data = malloc(node->fs_St.st_size)) == NULL) {
190 		fprintf(stderr, "Ran out of memory copying %s\n", path);
191 		free(node);
192 		node = NULL;
193 	    } else if ((n = open(path, O_RDONLY)) < 0) {
194 		fprintf(stderr, "Unable to open %s for reading\n", path);
195 		free(node->fs_Data);
196 		free(node);
197 		node = NULL;
198 	    } else if (read(n, node->fs_Data, node->fs_St.st_size) != node->fs_St.st_size) {
199 		fprintf(stderr, "Unable to read %s\n", path);
200 		free(node->fs_Data);
201 		free(node);
202 		node = NULL;
203 
204 	    } else {
205 		node->fs_Bytes = node->fs_St.st_size;
206 		if (node->fs_St.st_nlink > 1) {
207 		    node->fs_HNext = *phlinks;
208 		    *phlinks = node;
209 		}
210 	    }
211 	    break;
212 	case S_IFLNK:
213 	    if ((n = readlink(path, buf, sizeof(buf))) > 0) {
214 		if ((node->fs_Data = malloc(n + 1)) == NULL) {
215 		    fprintf(stderr, "Ran out of memory\n");
216 		    free(node);
217 		    node = NULL;
218 		} else {
219 		    node->fs_Bytes = n;
220 		    bcopy(buf, node->fs_Data, n);
221 		    node->fs_Data[n] = 0;
222 		}
223 	    } else if (n == 0) {
224 		node->fs_Data = "";
225 		node->fs_Bytes = 0;
226 	    } else {
227 		fprintf(stderr, "Unable to read link: %s\n", path);
228 		free(node);
229 		node = NULL;
230 	    }
231 	    break;
232 	case S_IFSOCK:
233 	    break;
234 	case S_IFWHT:
235 	    break;
236 	default:
237 	    break;
238 	}
239     }
240     return(node);
241 }
242 
243 void
244 FSPaste(const char *path, fsnode_t node, fsnode_t hlinks)
245 {
246     struct timeval times[2];
247     fsnode_t scan;
248     int fd;
249     int ok = 0;
250 
251     switch(node->fs_St.st_mode & S_IFMT) {
252     case S_IFIFO:
253 	break;
254     case S_IFCHR:
255     case S_IFBLK:
256 	if (mknod(path, node->fs_St.st_mode, node->fs_St.st_rdev) < 0) {
257 	    fprintf(stderr, "Paste: mknod failed on %s\n", path);
258 	    break;
259 	}
260 	ok = 1;
261 	break;
262     case S_IFDIR:
263 	fd = open(".", O_RDONLY);
264 	if (fd < 0) {
265 	    fprintf(stderr, "Paste: cannot open current directory\n");
266 	    exit(1);
267 	}
268 	if (mkdir(path, 0700) < 0 && errno != EEXIST) {
269 	    printf("Paste: unable to create directory %s\n", path);
270 	    close(fd);
271 	    break;
272 	}
273 	if (chdir(path) < 0) {
274 	    printf("Paste: unable to chdir into %s\n", path);
275 	    exit(1);
276 	}
277 	for (scan = node->fs_Base; scan; scan = scan->fs_Next) {
278 	    FSPaste(scan->fs_Name, scan, hlinks);
279 	}
280 	if (fchdir(fd) < 0) {
281 	    fprintf(stderr, "Paste: cannot fchdir current dir\n");
282 	    close(fd);
283 	    exit(1);
284 	}
285 	close(fd);
286 	ok = 1;
287 	break;
288     case S_IFREG:
289 	if (node->fs_St.st_nlink > 1 && node->fs_Bytes < 0) {
290 	    if ((scan = fsgethlink(hlinks, node)) == NULL) {
291 		fprintf(stderr, "Cannot find hardlink for %s\n", path);
292 	    } else {
293 		char *hpath = fshardpath(scan, node);
294 		if (hpath == NULL || link(hpath, path) < 0) {
295 		    fprintf(stderr, "Cannot create hardlink: %s->%s\n", path, hpath ? hpath : "?");
296 		    if (hpath)
297 			free(hpath);
298 		    break;
299 		}
300 		ok = 1;
301 		free(hpath);
302 	    }
303 	    break;
304 	}
305 	if ((fd = open(path, O_RDWR|O_CREAT|O_TRUNC, 0600)) < 0) {
306 	    fprintf(stderr, "Cannot create file: %s\n", path);
307 	    break;
308 	}
309 	if (write(fd, node->fs_Data, node->fs_Bytes) != node->fs_Bytes) {
310 	    fprintf(stderr, "Cannot write file: %s\n", path);
311 	    remove(path);
312 	    close(fd);
313 	    break;
314 	}
315 	close(fd);
316 	ok = 1;
317 	break;
318     case S_IFLNK:
319 	if (symlink(node->fs_Data, path) < 0) {
320 	    fprintf(stderr, "Unable to create symbolic link: %s\n", path);
321 	    break;
322 	}
323 	ok = 1;
324 	break;
325     case S_IFSOCK:
326 	break;
327     case S_IFWHT:
328 	break;
329     default:
330 	break;
331     }
332 
333     /*
334      * Set perms
335      */
336     if (ok) {
337 	struct stat *st = &node->fs_St;
338 
339 	times[0].tv_sec = st->st_atime;
340 	times[0].tv_usec = 0;
341 	times[1].tv_sec = st->st_mtime;
342 	times[1].tv_usec = 0;
343 
344 	if (lchown(path, st->st_uid, st->st_gid) < 0)
345 	    fprintf(stderr, "lchown failed on %s\n", path);
346 	if (lutimes(path, times) < 0)
347 	    fprintf(stderr, "lutimes failed on %s\n", path);
348 	if (lchmod(path, st->st_mode & ALLPERMS) < 0)
349 	    fprintf(stderr, "lchmod failed on %s\n", path);
350 	if (chflags(path, st->st_flags) < 0)
351 	    fprintf(stderr, "lchflags failed on %s\n", path);
352     }
353 }
354 
355