xref: /dragonfly/sbin/newfs/fscopy.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
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.7 2005/08/08 17:06:18 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
fsmknode(const char * path)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 static fsnode_t
fsgethlink(fsnode_t hlinks,fsnode_t node)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 static char *
fshardpath(fsnode_t hlink,fsnode_t node)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
FSCopy(fsnode_t * phlinks,const char * path)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 (strcmp(den->d_name, ".") == 0)
160                               continue;
161                         if (strcmp(den->d_name, "..") == 0)
162                               continue;
163                         scan = FSCopy(phlinks, den->d_name);
164                         if (scan) {
165                               *pscan = scan;
166                               scan->fs_Parent = node;
167                               pscan = &scan->fs_Next;
168                         }
169                     }
170                     if (chdir("..") < 0) {
171                         fprintf(stderr, "Unable to chdir .. after scanning %s\n", path);
172                         exit(1);
173                     }
174                     closedir(dir);
175               }
176               break;
177           case S_IFBLK:
178               break;
179           case S_IFREG:
180               if (node->fs_St.st_nlink > 1 && fsgethlink(*phlinks, node)) {
181                     node->fs_Bytes = -1;          /* hardlink indicator */
182               } else if (node->fs_St.st_size >= 0x80000000LL) {
183                     fprintf(stderr, "File %s too large to copy\n", path);
184                     free(node);
185                     node = NULL;
186               } else if ((node->fs_Data = malloc(node->fs_St.st_size)) == NULL) {
187                     fprintf(stderr, "Ran out of memory copying %s\n", path);
188                     free(node);
189                     node = NULL;
190               } else if ((n = open(path, O_RDONLY)) < 0) {
191                     fprintf(stderr, "Unable to open %s for reading\n", path);
192                     free(node->fs_Data);
193                     free(node);
194                     node = NULL;
195               } else if (read(n, node->fs_Data, node->fs_St.st_size) != node->fs_St.st_size) {
196                     fprintf(stderr, "Unable to read %s\n", path);
197                     free(node->fs_Data);
198                     free(node);
199                     node = NULL;
200 
201               } else {
202                     node->fs_Bytes = node->fs_St.st_size;
203                     if (node->fs_St.st_nlink > 1) {
204                         node->fs_HNext = *phlinks;
205                         *phlinks = node;
206                     }
207               }
208               break;
209           case S_IFLNK:
210               if ((n = readlink(path, buf, sizeof(buf))) > 0) {
211                     if ((node->fs_Data = malloc(n + 1)) == NULL) {
212                         fprintf(stderr, "Ran out of memory\n");
213                         free(node);
214                         node = NULL;
215                     } else {
216                         node->fs_Bytes = n;
217                         bcopy(buf, node->fs_Data, n);
218                         node->fs_Data[n] = 0;
219                     }
220               } else if (n == 0) {
221                     node->fs_Data = NULL;
222                     node->fs_Bytes = 0;
223               } else {
224                     fprintf(stderr, "Unable to read link: %s\n", path);
225                     free(node);
226                     node = NULL;
227               }
228               break;
229           case S_IFSOCK:
230               break;
231           case S_IFWHT:
232               break;
233           default:
234               break;
235           }
236     }
237     return(node);
238 }
239 
240 void
FSPaste(const char * path,fsnode_t node,fsnode_t hlinks)241 FSPaste(const char *path, fsnode_t node, fsnode_t hlinks)
242 {
243     struct timeval times[2];
244     fsnode_t scan;
245     int fd;
246     int ok = 0;
247 
248     switch(node->fs_St.st_mode & S_IFMT) {
249     case S_IFIFO:
250           break;
251     case S_IFCHR:
252     case S_IFBLK:
253           if (mknod(path, node->fs_St.st_mode, node->fs_St.st_rdev) < 0) {
254               fprintf(stderr, "Paste: mknod failed on %s\n", path);
255               break;
256           }
257           ok = 1;
258           break;
259     case S_IFDIR:
260           fd = open(".", O_RDONLY);
261           if (fd < 0) {
262               fprintf(stderr, "Paste: cannot open current directory\n");
263               exit(1);
264           }
265           if (mkdir(path, 0700) < 0 && errno != EEXIST) {
266               printf("Paste: unable to create directory %s\n", path);
267               close(fd);
268               break;
269           }
270           if (chdir(path) < 0) {
271               printf("Paste: unable to chdir into %s\n", path);
272               exit(1);
273           }
274           for (scan = node->fs_Base; scan; scan = scan->fs_Next) {
275               FSPaste(scan->fs_Name, scan, hlinks);
276           }
277           if (fchdir(fd) < 0) {
278               fprintf(stderr, "Paste: cannot fchdir current dir\n");
279               close(fd);
280               exit(1);
281           }
282           close(fd);
283           ok = 1;
284           break;
285     case S_IFREG:
286           if (node->fs_St.st_nlink > 1 && node->fs_Bytes < 0) {
287               if ((scan = fsgethlink(hlinks, node)) == NULL) {
288                     fprintf(stderr, "Cannot find hardlink for %s\n", path);
289               } else {
290                     char *hpath = fshardpath(scan, node);
291                     if (hpath == NULL || link(hpath, path) < 0) {
292                         fprintf(stderr, "Cannot create hardlink: %s->%s\n", path, hpath ? hpath : "?");
293                         if (hpath)
294                               free(hpath);
295                         break;
296                     }
297                     ok = 1;
298                     free(hpath);
299               }
300               break;
301           }
302           if ((fd = open(path, O_RDWR|O_CREAT|O_TRUNC, 0600)) < 0) {
303               fprintf(stderr, "Cannot create file: %s\n", path);
304               break;
305           }
306           if (node->fs_Bytes > 0) {
307               if (write(fd, node->fs_Data, node->fs_Bytes) != node->fs_Bytes) {
308                     fprintf(stderr, "Cannot write file: %s\n", path);
309                     remove(path);
310                     close(fd);
311                     break;
312               }
313           }
314           close(fd);
315           ok = 1;
316           break;
317     case S_IFLNK:
318           if (symlink(node->fs_Bytes > 0 ? node->fs_Data : "", path) < 0) {
319               fprintf(stderr, "Unable to create symbolic link: %s\n", path);
320               break;
321           }
322           ok = 1;
323           break;
324     case S_IFSOCK:
325           break;
326     case S_IFWHT:
327           break;
328     default:
329           break;
330     }
331 
332     /*
333      * Set perms
334      */
335     if (ok) {
336           struct stat *st = &node->fs_St;
337 
338           times[0].tv_sec = st->st_atime;
339           times[0].tv_usec = 0;
340           times[1].tv_sec = st->st_mtime;
341           times[1].tv_usec = 0;
342 
343           if (lchown(path, st->st_uid, st->st_gid) < 0)
344               fprintf(stderr, "lchown failed on %s\n", path);
345           if (lutimes(path, times) < 0)
346               fprintf(stderr, "lutimes failed on %s\n", path);
347           if (lchmod(path, st->st_mode & ALLPERMS) < 0)
348               fprintf(stderr, "lchmod failed on %s\n", path);
349           if (!S_ISLNK(st->st_mode)) {
350               if (chflags(path, st->st_flags) < 0)
351                     fprintf(stderr, "chflags failed on %s\n", path);
352           }
353     }
354 }
355 
356