summary refs log tree commit diff
path: root/src/libstddjb/hiercopy_tmp.c
blob: bcff2b223601325abf2753fc9ee4cb1d54bd9f82 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/* ISC license. */

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <skalibs/bytestr.h>
#include <skalibs/strerr2.h>
#include <skalibs/stralloc.h>
#include <skalibs/direntry.h>
#include <skalibs/djbunix.h>

static int dircopy (char const *src, char const *dst, mode_t mode, stralloc *tmp)
{
  unsigned int tmpbase = tmp->len ;
  unsigned int maxlen = 0 ;
  {
    DIR *dir = opendir(src) ;
    if (!dir) return 0 ;
    for (;;)
    {
      direntry *d ;
      register unsigned int n ;
      errno = 0 ;
      d = readdir(dir) ;
      if (!d) break ;
      if (d->d_name[0] == '.')
        if (((d->d_name[1] == '.') && !d->d_name[2]) || !d->d_name[1])
          continue ;
      n = str_len(d->d_name) ;
      if (n > maxlen) maxlen = n ;
      if (!stralloc_catb(tmp, d->d_name, n+1)) break ;
    }
    if (errno)
    {
      int e = errno ;
      dir_close(dir) ;
      errno = e ;
      goto err ;
    }
    dir_close(dir) ;
  }

  if (mkdir(dst, S_IRWXU) < 0)
  {
    struct stat st ;
    if (errno != EEXIST) goto err ;
    if (stat(dst, &st) < 0) goto err ;
    if (!S_ISDIR(st.st_mode)) { errno = ENOTDIR ; goto err ; }
  }
  {
    unsigned int srclen = str_len(src) ;
    unsigned int dstlen = str_len(dst) ;
    unsigned int i = tmpbase ;
    char srcbuf[srclen + maxlen + 2] ;
    char dstbuf[dstlen + maxlen + 2] ;
    byte_copy(srcbuf, srclen, src) ;
    byte_copy(dstbuf, dstlen, dst) ;
    srcbuf[srclen] = '/' ;
    dstbuf[dstlen] = '/' ;
    while (i < tmp->len)
    {
      register unsigned int n = str_len(tmp->s + i) + 1 ;
      byte_copy(srcbuf + srclen + 1, n, tmp->s + i) ;
      byte_copy(dstbuf + dstlen + 1, n, tmp->s + i) ;
      i += n ;
      hiercopy_tmp(srcbuf, dstbuf, tmp) ;
    }
  }
  if (chmod(dst, mode) < 0) goto err ;
  tmp->len = tmpbase ;
  return 1 ;
err:
  tmp->len = tmpbase ;
  return 0 ;
}

int hiercopy_tmp (char const *src, char const *dst, stralloc *tmp)
{
  struct stat st ;
  if (lstat(src, &st) < 0) return 0 ;
  if (S_ISREG(st.st_mode))
  {
    if (!filecopy_unsafe(src, dst, st.st_mode)) return 0 ;
  }
  else if (S_ISDIR(st.st_mode))
  {
    if (!dircopy(src, dst, st.st_mode, tmp)) return 0 ;
  }
  else if (S_ISFIFO(st.st_mode))
  {
    if (mkfifo(dst, st.st_mode) < 0) return 0 ;
  }
  else if (S_ISLNK(st.st_mode))
  {
    unsigned int tmpbase = tmp->len ;
    if (sareadlink(tmp, src) < 0) return 0 ;
    if (!stralloc_0(tmp))
    {
      tmp->len = tmpbase ;
      return 0 ;
    }
    if (symlink(tmp->s + tmpbase, dst) < 0)
    {
      tmp->len = tmpbase ;
      return 0 ;
    }
    tmp->len = tmpbase ;
  }
  else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) || S_ISSOCK(st.st_mode))
  {
    if (mknod(dst, st.st_mode, st.st_rdev) < 0) return 0 ;
  }
  else return (errno = ENOTSUP, 0) ;
  lchown(dst, st.st_uid, st.st_gid) ;
  if (!S_ISLNK(st.st_mode)) chmod(dst, st.st_mode) ;
  return 1 ;
}