1 /* ISC license. */
2 
3 #include <string.h>
4 #include <unistd.h>
5 #include <sys/stat.h>
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <skalibs/strerr2.h>
9 #include <skalibs/stralloc.h>
10 #include <skalibs/direntry.h>
11 #include <skalibs/djbunix.h>
12 
dircopy(char const * src,char const * dst,mode_t mode,stralloc * tmp)13 static int dircopy (char const *src, char const *dst, mode_t mode, stralloc *tmp)
14 {
15   size_t tmpbase = tmp->len ;
16   size_t maxlen = 0 ;
17   {
18     DIR *dir = opendir(src) ;
19     if (!dir) return 0 ;
20     for (;;)
21     {
22       direntry *d ;
23       size_t n ;
24       errno = 0 ;
25       d = readdir(dir) ;
26       if (!d) break ;
27       if (d->d_name[0] == '.')
28         if (((d->d_name[1] == '.') && !d->d_name[2]) || !d->d_name[1])
29           continue ;
30       n = strlen(d->d_name) ;
31       if (n > maxlen) maxlen = n ;
32       if (!stralloc_catb(tmp, d->d_name, n+1)) break ;
33     }
34     if (errno)
35     {
36       int e = errno ;
37       dir_close(dir) ;
38       errno = e ;
39       goto err ;
40     }
41     dir_close(dir) ;
42   }
43 
44   if (mkdir(dst, S_IRWXU) < 0)
45   {
46     struct stat st ;
47     if (errno != EEXIST) goto err ;
48     if (stat(dst, &st) < 0) goto err ;
49     if (!S_ISDIR(st.st_mode)) { errno = ENOTDIR ; goto err ; }
50   }
51   {
52     size_t srclen = strlen(src) ;
53     size_t dstlen = strlen(dst) ;
54     size_t i = tmpbase ;
55     char srcbuf[srclen + maxlen + 2] ;
56     char dstbuf[dstlen + maxlen + 2] ;
57     memcpy(srcbuf, src, srclen) ;
58     memcpy(dstbuf, dst, dstlen) ;
59     srcbuf[srclen] = '/' ;
60     dstbuf[dstlen] = '/' ;
61     while (i < tmp->len)
62     {
63       size_t n = strlen(tmp->s + i) + 1 ;
64       memcpy(srcbuf + srclen + 1, tmp->s + i, n) ;
65       memcpy(dstbuf + dstlen + 1, tmp->s + i, n) ;
66       i += n ;
67       hiercopy_tmp(srcbuf, dstbuf, tmp) ;
68     }
69   }
70   if (chmod(dst, mode) < 0) goto err ;
71   tmp->len = tmpbase ;
72   return 1 ;
73 err:
74   tmp->len = tmpbase ;
75   return 0 ;
76 }
77 
hiercopy_tmp(char const * src,char const * dst,stralloc * tmp)78 int hiercopy_tmp (char const *src, char const *dst, stralloc *tmp)
79 {
80   struct stat st ;
81   if (lstat(src, &st) < 0) return 0 ;
82   if (S_ISREG(st.st_mode))
83   {
84     if (!filecopy_unsafe(src, dst, st.st_mode)) return 0 ;
85   }
86   else if (S_ISDIR(st.st_mode))
87   {
88     if (!dircopy(src, dst, st.st_mode, tmp)) return 0 ;
89   }
90   else if (S_ISFIFO(st.st_mode))
91   {
92     if (mkfifo(dst, st.st_mode) < 0) return 0 ;
93   }
94   else if (S_ISLNK(st.st_mode))
95   {
96     size_t tmpbase = tmp->len ;
97     if (sareadlink(tmp, src) < 0) return 0 ;
98     if (!stralloc_0(tmp) || symlink(tmp->s + tmpbase, dst) < 0)
99     {
100       tmp->len = tmpbase ;
101       return 0 ;
102     }
103     tmp->len = tmpbase ;
104   }
105   else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) || S_ISSOCK(st.st_mode))
106   {
107     if (mknod(dst, st.st_mode, st.st_rdev) < 0) return 0 ;
108   }
109   else return (errno = ENOTSUP, 0) ;
110   lchown(dst, st.st_uid, st.st_gid) ;
111   if (!S_ISLNK(st.st_mode)) chmod(dst, st.st_mode) ;
112   return 1 ;
113 }
114