1    	/*
2    	    Authors:
3    	        Jakub Hrozek <jhrozek@redhat.com>
4    	
5    	    Copyright (C) 2009 Red Hat
6    	
7    	    This program is free software; you can redistribute it and/or modify
8    	    it under the terms of the GNU General Public License as published by
9    	    the Free Software Foundation; either version 3 of the License, or
10   	    (at your option) any later version.
11   	
12   	    This program is distributed in the hope that it will be useful,
13   	    but WITHOUT ANY WARRANTY; without even the implied warranty of
14   	    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   	    GNU General Public License for more details.
16   	
17   	    You should have received a copy of the GNU General Public License
18   	    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19   	*/
20   	
21   	/*
22   	 * This file incorporates work covered by the following copyright and
23   	 * permission notice:
24   	 *
25   	 * Copyright (c) 1991 - 1994, Julianne Frances Haugh
26   	 * Copyright (c) 1996 - 2001, Marek Michałkiewicz
27   	 * Copyright (c) 2003 - 2006, Tomasz Kłoczko
28   	 * Copyright (c) 2007 - 2008, Nicolas François
29   	 *
30   	 * All rights reserved.
31   	 *
32   	 * Redistribution and use in source and binary forms, with or without
33   	 * modification, are permitted provided that the following conditions
34   	 * are met:
35   	 * 1. Redistributions of source code must retain the above copyright
36   	 *    notice, this list of conditions and the following disclaimer.
37   	 * 2. Redistributions in binary form must reproduce the above copyright
38   	 *    notice, this list of conditions and the following disclaimer in the
39   	 *    documentation and/or other materials provided with the distribution.
40   	 * 3. The name of the copyright holders or contributors may not be used to
41   	 *    endorse or promote products derived from this software without
42   	 *    specific prior written permission.
43   	 *
44   	 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
45   	 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
46   	 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
47   	 * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
48   	 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
49   	 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
50   	 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
51   	 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
52   	 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
53   	 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
54   	 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
55   	 */
56   	
57   	#include <sys/stat.h>
58   	#include <sys/types.h>
59   	#include <sys/time.h>
60   	#include <dirent.h>
61   	#include <fcntl.h>
62   	#include <errno.h>
63   	#include <talloc.h>
64   	
65   	#include "config.h"
66   	#include "util/util.h"
67   	#include "tools/tools_util.h"
68   	
69   	int copy_tree(const char *src_root, const char *dst_root,
70   	              uid_t uid, gid_t gid);
71   	
72   	struct copy_ctx {
73   	    const char *src_orig;
74   	    const char *dst_orig;
75   	    dev_t       src_dev;
76   	};
77   	
78   	/* wrapper in order not to create a temporary context in
79   	 * every iteration */
80   	static int remove_tree_with_ctx(TALLOC_CTX *mem_ctx,
81   	                                dev_t parent_dev,
82   	                                const char *root);
83   	
84   	int remove_tree(const char *root)
85   	{
86   	    TALLOC_CTX *tmp_ctx = NULL;
87   	    int ret;
88   	
89   	    tmp_ctx = talloc_new(NULL);
90   	    if (!tmp_ctx) {
91   	        return ENOMEM;
92   	    }
93   	
94   	    ret = remove_tree_with_ctx(tmp_ctx, 0, root);
95   	    talloc_free(tmp_ctx);
96   	    return ret;
97   	}
98   	
99   	/*
100  	 * The context is not freed in case of error
101  	 * because this is a recursive function, will be freed when we
102  	 * reach the top level remove_tree() again
103  	 */
104  	static int remove_tree_with_ctx(TALLOC_CTX *mem_ctx,
105  	                                dev_t parent_dev,
106  	                                const char *root)
107  	{
108  	    char *fullpath = NULL;
109  	    struct dirent *result;
110  	    struct dirent direntp;
111  	    struct stat statres;
112  	    DIR *rootdir = NULL;
113  	    int ret;
114  	
Event alloc_fn: Calling allocation function "opendir".
Event var_assign: Assigning: "rootdir" = storage returned from "opendir(root)".
Also see events: [leaked_storage][noescape]
115  	    rootdir = opendir(root);
At conditional (1): "rootdir == NULL": Taking false branch.
116  	    if (rootdir == NULL) {
117  	        ret = errno;
118  	        DEBUG(1, ("Cannot open directory %s [%d][%s]\n",
119  	                  root, ret, strerror(ret)));
120  	        goto fail;
121  	    }
122  	
Event noescape: Variable "rootdir" is not freed or pointed-to in function "readdir_r".
Also see events: [alloc_fn][var_assign][leaked_storage]
At conditional (2): "readdir_r(rootdir, &direntp, &result) == 0": Taking true branch.
123  	    while (readdir_r(rootdir, &direntp, &result) == 0) {
At conditional (3): "result == NULL": Taking false branch.
124  	        if (result == NULL) {
125  	            /* End of directory */
126  	            break;
127  	        }
128  	
At conditional (4): "strcmp(direntp.d_name, &".") == 0": Taking false branch.
At conditional (5): "strcmp(direntp.d_name, &"..") == 0": Taking false branch.
129  	        if (strcmp (direntp.d_name, ".") == 0 ||
130  	            strcmp (direntp.d_name, "..") == 0) {
131  	            continue;
132  	        }
133  	
134  	        fullpath = talloc_asprintf(mem_ctx, "%s/%s", root, direntp.d_name);
At conditional (6): "fullpath == NULL": Taking true branch.
135  	        if (fullpath == NULL) {
136  	            ret = ENOMEM;
137  	            goto fail;
138  	        }
139  	
140  	        ret = lstat(fullpath, &statres);
141  	        if (ret != 0) {
142  	            ret = errno;
143  	            DEBUG(1, ("Cannot stat %s: [%d][%s]\n",
144  	                      fullpath, ret, strerror(ret)));
145  	            goto fail;
146  	        }
147  	
148  	        if (S_ISDIR(statres.st_mode)) {
149  	            /* if directory, recursively descend, but check if on the same FS */
150  	            if (parent_dev && parent_dev != statres.st_dev) {
151  	                DEBUG(1, ("Directory %s is on different filesystem, "
152  	                          "will not follow\n", fullpath));
153  	                ret = EFAULT;
154  	                goto fail;
155  	            }
156  	
157  	            ret = remove_tree_with_ctx(mem_ctx, statres.st_dev, fullpath);
158  	            if (ret != EOK) {
159  	                DEBUG(1, ("Removing subdirectory %s failed: [%d][%s]\n",
160  	                            fullpath, ret, strerror(ret)));
161  	                goto fail;
162  	            }
163  	        } else {
164  	            ret = unlink(fullpath);
165  	            if (ret != 0) {
166  	                ret = errno;
167  	                DEBUG(1, ("Removing file %s failed: [%d][%s]\n",
168  	                          fullpath, ret, strerror(ret)));
169  	                goto fail;
170  	            }
171  	        }
172  	
173  	        talloc_free(fullpath);
174  	    }
175  	
176  	    ret = closedir(rootdir);
177  	    if (ret != 0) {
178  	        ret = errno;
179  	        goto fail;
180  	    }
181  	
182  	    ret = rmdir(root);
183  	    if (ret != 0) {
184  	        ret = errno;
185  	        goto fail;
186  	    }
187  	
188  	fail:
Event leaked_storage: Variable "rootdir" going out of scope leaks the storage it points to.
Also see events: [alloc_fn][var_assign][noescape]
189  	    return ret;
190  	}
191  	
192  	static int copy_dir(const char *src, const char *dst,
193  	                    const struct stat *statp, const struct timeval mt[2],
194  	                    uid_t uid, gid_t gid)
195  	{
196  	    int ret = 0;
197  	
198  	    /*
199  	     * Create a new target directory, make it owned by
200  	     * the user and then recursively copy that directory.
201  	     */
202  	    selinux_file_context(dst);
203  	
204  	    ret = mkdir(dst, statp->st_mode);
205  	    if (ret != 0) {
206  	        ret = errno;
207  	        DEBUG(1, ("Cannot mkdir directory '%s': [%d][%s].\n",
208  	                  dst, ret, strerror(ret)));
209  	        return ret;
210  	    }
211  	
212  	    ret = chown(dst, uid, gid);
213  	    if (ret != 0) {
214  	        ret = errno;
215  	        DEBUG(1, ("Cannot chown directory '%s': [%d][%s].\n",
216  	                  dst, ret, strerror(ret)));
217  	        return ret;
218  	    }
219  	
220  	    ret = chmod(dst, statp->st_mode);
221  	    if (ret != 0) {
222  	        ret = errno;
223  	        DEBUG(1, ("Cannot chmod directory '%s': [%d][%s].\n",
224  	                  dst, ret, strerror(ret)));
225  	        return ret;
226  	    }
227  	
228  	    ret = copy_tree(src, dst, uid, gid);
229  	    if (ret != 0) {
230  	        ret = errno;
231  	        DEBUG(1, ("Cannot copy directory from '%s' to '%s': [%d][%s].\n",
232  	                  src, dst, ret, strerror(ret)));
233  	        return ret;
234  	    }
235  	
236  	    ret = utimes(dst, mt);
237  	    if (ret != 0) {
238  	        ret = errno;
239  	        DEBUG(1, ("Cannot set utimes on a directory '%s': [%d][%s].\n",
240  	                  dst, ret, strerror(ret)));
241  	        return ret;
242  	    }
243  	
244  	    return EOK;
245  	}
246  	
247  	static char *talloc_readlink(TALLOC_CTX *mem_ctx, const char *filename)
248  	{
249  	    size_t size = 1024;
250  	    ssize_t nchars;
251  	    char *buffer;
252  	
253  	    buffer = talloc_array(mem_ctx, char, size);
254  	    if (!buffer) {
255  	        return NULL;
256  	    }
257  	
258  	    while (1) {
259  	        nchars = readlink(filename, buffer, size);
260  	        if (nchars < 0) {
261  	            return NULL;
262  	        }
263  	
264  	        if ((size_t) nchars < size) {
265  	            /* The buffer was large enough */
266  	            break;
267  	        }
268  	
269  	        /* Try again with a bigger buffer */
270  	        size *= 2;
271  	        buffer = talloc_realloc(mem_ctx, buffer, char, size);
272  	        if (!buffer) {
273  	            return NULL;
274  	        }
275  	    }
276  	
277  	    /* readlink does not nul-terminate */
278  	    buffer[nchars] = '\0';
279  	    return buffer;
280  	}
281  	
282  	static int copy_symlink(struct copy_ctx *cctx,
283  	                        const char *src,
284  	                        const char *dst,
285  	                        const struct stat *statp,
286  	                        const struct timeval mt[],
287  	                        uid_t uid, gid_t gid)
288  	{
289  	    int ret;
290  	    char *oldlink;
291  	    char *tmp;
292  	    TALLOC_CTX *tmp_ctx = NULL;
293  	
294  	    tmp_ctx = talloc_new(cctx);
295  	    if (!tmp_ctx) {
296  	        return ENOMEM;
297  	    }
298  	
299  	    /*
300  	     * Get the name of the file which the link points
301  	     * to.  If that name begins with the original
302  	     * source directory name, that part of the link
303  	     * name will be replaced with the original
304  	     * destination directory name.
305  	     */
306  	    oldlink = talloc_readlink(tmp_ctx, src);
307  	    if (oldlink == NULL) {
308  	        ret = ENOMEM;
309  	        goto done;
310  	    }
311  	
312  	    /* If src was a link to an entry of the src_orig directory itself,
313  	     * create a link to the corresponding entry in the dst_orig
314  	     * directory.
315  	     * FIXME: This may change a relative link to an absolute link
316  	     */
317  	    if (strncmp(oldlink, cctx->src_orig, strlen(cctx->src_orig)) == 0) {
318  	        tmp = talloc_asprintf(tmp_ctx, "%s%s", cctx->dst_orig, oldlink + strlen(cctx->src_orig));
319  	        if (tmp == NULL) {
320  	            ret = ENOMEM;
321  	            goto done;
322  	        }
323  	
324  	        talloc_free(oldlink);
325  	        oldlink = tmp;
326  	    }
327  	
328  	    selinux_file_context(dst);
329  	
330  	    ret = symlink(oldlink, dst);
331  	    if (ret != 0) {
332  	        ret = errno;
333  	        DEBUG(1, ("symlink() failed on file '%s': [%d][%s].\n",
334  	                  dst, ret, strerror(ret)));
335  	        goto done;
336  	    }
337  	
338  	    ret = lchown(dst, uid, gid);
339  	    if (ret != 0) {
340  	        ret = errno;
341  	        DEBUG(1, ("lchown() failed on file '%s': [%d][%s].\n",
342  	                  dst, ret, strerror(ret)));
343  	        goto done;
344  	    }
345  	
346  	done:
347  	    talloc_free(tmp_ctx);
348  	    return ret;
349  	}
350  	
351  	static int copy_special(const char *dst,
352  	                        const struct stat *statp,
353  	                        const struct timeval mt[],
354  	                        uid_t uid, gid_t gid)
355  	{
356  	    int ret = 0;
357  	
358  	    selinux_file_context(dst);
359  	
360  	    ret = mknod(dst, statp->st_mode & ~07777, statp->st_rdev);
361  	    if (ret != 0) {
362  	        ret = errno;
363  	        DEBUG(1, ("Cannot mknod special file '%s': [%d][%s].\n",
364  	                  dst, ret, strerror(ret)));
365  	        return ret;
366  	    }
367  	
368  	    ret = chown(dst, uid, gid);
369  	    if (ret != 0) {
370  	        ret = errno;
371  	        DEBUG(1, ("Cannot chown special file '%s': [%d][%s].\n",
372  	                  dst, ret, strerror(ret)));
373  	        return ret;
374  	    }
375  	
376  	    ret = chmod(dst, statp->st_mode & 07777);
377  	    if (ret != 0) {
378  	        ret = errno;
379  	        DEBUG(1, ("Cannot chmod special file '%s': [%d][%s].\n",
380  	                  dst, ret, strerror(ret)));
381  	        return ret;
382  	    }
383  	
384  	    ret = utimes(dst, mt);
385  	    if (ret != 0) {
386  	        ret = errno;
387  	        DEBUG(1, ("Cannot call utimes on special file '%s': [%d][%s].\n",
388  	                  dst, ret, strerror(ret)));
389  	        return ret;
390  	    }
391  	
392  	    return EOK;
393  	}
394  	
395  	static int copy_file(const char *src,
396  	                     const char *dst,
397  	                     const struct stat *statp,
398  	                     const struct timeval mt[],
399  	                     uid_t uid, gid_t gid)
400  	{
401  	    int ret;
402  	    int ifd = -1;
403  	    int ofd = -1;
404  	    char buf[1024];
405  	    ssize_t cnt, written, offset;
406  	    struct stat fstatbuf;
407  	
408  	    ifd = open(src, O_RDONLY);
409  	    if (ifd < 0) {
410  	        ret = errno;
411  	        DEBUG(1, ("Cannot open() source file '%s': [%d][%s].\n",
412  	                  src, ret, strerror(ret)));
413  	        goto fail;
414  	    }
415  	
416  	    ret = fstat(ifd, &fstatbuf);
417  	    if (ret != 0) {
418  	        ret = errno;
419  	        DEBUG(1, ("Cannot fstat() source file '%s': [%d][%s].\n",
420  	                  src, ret, strerror(ret)));
421  	        goto fail;
422  	    }
423  	
424  	    if (statp->st_dev != fstatbuf.st_dev ||
425  	        statp->st_ino != fstatbuf.st_ino) {
426  	        DEBUG(1, ("File %s was modified between lstat and open.\n", src));
427  	        ret = EIO;
428  	        goto fail;
429  	    }
430  	
431  	    selinux_file_context(dst);
432  	
433  	    ofd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, statp->st_mode & 07777);
434  	    if (ofd < 0) {
435  	        ret = errno;
436  	        DEBUG(1, ("Cannot open() destination file '%s': [%d][%s].\n",
437  	                  dst, ret, strerror(ret)));
438  	        goto fail;
439  	    }
440  	
441  	    ret = fchown(ofd, uid, gid);
442  	    if (ret != 0) {
443  	        ret = errno;
444  	        DEBUG(1, ("Cannot fchown() destination file '%s': [%d][%s].\n",
445  	                  dst, ret, strerror(ret)));
446  	        goto fail;
447  	    }
448  	
449  	    ret = fchmod(ofd, statp->st_mode & 07777);
450  	    if (ret != 0) {
451  	        ret = errno;
452  	        DEBUG(1, ("Cannot fchmod() destination file '%s': [%d][%s].\n",
453  	                  dst, ret, strerror(ret)));
454  	        goto fail;
455  	    }
456  	
457  	    while ((cnt = read(ifd, buf, sizeof(buf))) > 0) {
458  	        offset = 0;
459  	        while (cnt > 0) {
460  	            written = write(ofd, buf+offset, (size_t)cnt);
461  	            if (written == -1) {
462  	                ret = errno;
463  	                DEBUG(1, ("Cannot write() to source file '%s': [%d][%s].\n",
464  	                            dst, ret, strerror(ret)));
465  	                goto fail;
466  	            }
467  	            offset += written;
468  	            cnt -= written;
469  	        }
470  	    }
471  	    if (cnt == -1) {
472  	        ret = errno;
473  	        DEBUG(1, ("Cannot read() from source file '%s': [%d][%s].\n",
474  	                    dst, ret, strerror(ret)));
475  	        goto fail;
476  	    }
477  	
478  	
479  	    ret = close(ifd);
480  	    ifd = -1;
481  	    if (ret != 0) {
482  	        ret = errno;
483  	        DEBUG(1, ("Cannot close() source file '%s': [%d][%s].\n",
484  	                  dst, ret, strerror(ret)));
485  	        goto fail;
486  	    }
487  	
488  	    ret = close(ofd);
489  	    ifd = -1;
490  	    if (ret != 0) {
491  	        ret = errno;
492  	        DEBUG(1, ("Cannot close() destination file '%s': [%d][%s].\n",
493  	                  dst, ret, strerror(ret)));
494  	        goto fail;
495  	    }
496  	
497  	    ret = utimes(dst, mt);
498  	    if (ret != 0) {
499  	        ret = errno;
500  	        DEBUG(1, ("Cannot call utimes() on destination file '%s': [%d][%s].\n",
501  	                  dst, ret, strerror(ret)));
502  	        goto fail;
503  	    }
504  	
505  	    return EOK;
506  	
507  	    /* Reachable by jump only */
508  	fail:
509  	    if (ifd != -1) close(ifd);
510  	    if (ofd != -1) close(ofd);
511  	    return ret;
512  	}
513  	
514  	/*
515  	 * The context is not freed in case of error
516  	 * because this is a recursive function, will be freed when we
517  	 * reach the top level copy_tree() again
518  	 */
519  	static int copy_entry(struct copy_ctx *cctx,
520  	                      const char *src,
521  	                      const char *dst,
522  	                      uid_t uid,
523  	                      gid_t gid)
524  	{
525  	    int ret = EOK;
526  	    struct stat sb;
527  	    struct timeval mt[2];
528  	
529  	    ret = lstat(src, &sb);
530  	    if (ret == -1) {
531  	        ret = errno;
532  	        DEBUG(1, ("Cannot lstat() the source file '%s': [%d][%s].\n",
533  	                  src, ret, strerror(ret)));
534  	        return ret;
535  	    }
536  	
537  	    mt[0].tv_sec  = sb.st_atime;
538  	    mt[0].tv_usec = 0;
539  	
540  	    mt[1].tv_sec  = sb.st_mtime;
541  	    mt[1].tv_usec = 0;
542  	
543  	    if (S_ISLNK (sb.st_mode)) {
544  	        ret = copy_symlink(cctx, src, dst, &sb, mt, uid, gid);
545  	        if (ret != EOK) {
546  	            DEBUG(1, ("Cannot copy symlink '%s' to '%s': [%d][%s]\n",
547  	                      src, dst, ret, strerror(ret)));
548  	        }
549  	        return ret;
550  	    }
551  	
552  	    if (S_ISDIR(sb.st_mode)) {
553  	        /* Check if we're still on the same FS */
554  	        if (sb.st_dev != cctx->src_dev) {
555  	            DEBUG(2, ("Will not descend to other FS\n"));
556  	            /* Skip this without error */
557  	            return EOK;
558  	        }
559  	        return copy_dir(src, dst, &sb, mt, uid, gid);
560  	    } else if (!S_ISREG(sb.st_mode)) {
561  	        /*
562  	         * Deal with FIFOs and special files.  The user really
563  	         * shouldn't have any of these, but it seems like it
564  	         * would be nice to copy everything ...
565  	         */
566  	        return copy_special(dst, &sb, mt, uid, gid);
567  	    } else {
568  	        /*
569  	         * Create the new file and copy the contents.  The new
570  	         * file will be owned by the provided UID and GID values.
571  	         */
572  	        return copy_file(src, dst, &sb, mt, uid, gid);
573  	    }
574  	
575  	    return ret;
576  	}
577  	
578  	/*
579  	 * The context is not freed in case of error
580  	 * because this is a recursive function, will be freed when we
581  	 * reach the top level copy_tree() again
582  	 */
583  	static int copy_tree_ctx(struct copy_ctx *cctx,
584  	                         const char *src_root,
585  	                         const char *dst_root,
586  	                         uid_t uid,
587  	                         gid_t gid)
588  	{
589  	    DIR *src_dir;
590  	    int ret;
591  	    struct dirent *result;
592  	    struct dirent direntp;
593  	    char *src_name, *dst_name;
594  	    TALLOC_CTX *tmp_ctx;
595  	
596  	    tmp_ctx = talloc_new(cctx);
597  	
598  	    src_dir = opendir(src_root);
599  	    if (src_dir == NULL) {
600  	        ret = errno;
601  	        DEBUG(1, ("Cannot open the source directory %s: [%d][%s].\n",
602  	                  src_root, ret, strerror(ret)));
603  	        goto fail;
604  	    }
605  	
606  	    while (readdir_r(src_dir, &direntp, &result) == 0) {
607  	        if (result == NULL) {
608  	            /* End of directory */
609  	            break;
610  	        }
611  	
612  	        if (strcmp (direntp.d_name, ".") == 0 ||
613  	            strcmp (direntp.d_name, "..") == 0) {
614  	            continue;
615  	        }
616  	
617  	        /* build src and dst paths */
618  	        src_name = talloc_asprintf(tmp_ctx, "%s/%s", src_root, direntp.d_name);
619  	        dst_name = talloc_asprintf(tmp_ctx, "%s/%s", dst_root, direntp.d_name);
620  	        if (dst_name == NULL || src_name == NULL) {
621  	            ret = ENOMEM;
622  	            goto fail;
623  	        }
624  	
625  	        /* copy */
626  	        ret = copy_entry(cctx, src_name, dst_name, uid, gid);
627  	        if (ret != EOK) {
628  	            DEBUG(1, ("Cannot copy '%s' to '%s', error %d\n",
629  	                      src_name, dst_name, ret));
630  	            goto fail;
631  	        }
632  	        talloc_free(src_name);
633  	        talloc_free(dst_name);
634  	    }
635  	
636  	    ret = closedir(src_dir);
637  	    if (ret != 0) {
638  	        ret = errno;
639  	        goto fail;
640  	    }
641  	
642  	fail:
643  	    talloc_free(tmp_ctx);
644  	    return ret;
645  	}
646  	
647  	int copy_tree(const char *src_root, const char *dst_root,
648  	              uid_t uid, gid_t gid)
649  	{
650  	    int ret = EOK;
651  	    struct copy_ctx *cctx = NULL;
652  	    struct stat s_src;
653  	
654  	    cctx = talloc_zero(NULL, struct copy_ctx);
655  	
656  	    ret = lstat(src_root, &s_src);
657  	    if (ret != 0) {
658  	        ret = errno;
659  	        DEBUG(1, ("Cannot lstat the source directory '%s': [%d][%s]\n",
660  	                  src_root, ret, strerror(ret)));
661  	        goto fail;
662  	    }
663  	
664  	    cctx->src_orig = src_root;
665  	    cctx->dst_orig = dst_root;
666  	    cctx->src_dev  = s_src.st_dev;
667  	
668  	    ret = copy_tree_ctx(cctx, src_root, dst_root, uid, gid);
669  	    if (ret != EOK) {
670  	        DEBUG(1, ("copy_tree_ctx failed: [%d][%s]\n", ret, strerror(ret)));
671  	        goto fail;
672  	    }
673  	
674  	fail:
675  	    reset_selinux_file_context();
676  	    talloc_free(cctx);
677  	    return ret;
678  	}
679