about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--doc/libstddjb/djbunix.html17
-rw-r--r--doc/upgrade.html1
-rw-r--r--package/deps.mak2
-rw-r--r--src/include/skalibs/djbunix.h3
-rw-r--r--src/libstddjb/hiercopy.c9
-rw-r--r--src/libstddjb/hiercopy_tmp.c145
6 files changed, 177 insertions, 0 deletions
diff --git a/doc/libstddjb/djbunix.html b/doc/libstddjb/djbunix.html
index 277db31..ff8b27d 100644
--- a/doc/libstddjb/djbunix.html
+++ b/doc/libstddjb/djbunix.html
@@ -708,6 +708,23 @@ using *<em>tmp</em> as heap-allocated temporary space.
 Returns 0 if it succeeds or -1 (and sets errno) if it fails.
 </p>
 
+<h3> Filesystem copy </h3>
+
+<p>
+<code> int hiercopy_tmp (char const *src, char const *dst, stralloc *tmp) </code> <br />
+Recursively copies the filesystem hierarchy at <em>src</em> into
+<em>dst</em>, preserving modes, and also preserving the uids/gids if the
+process is running as the super-user.
+Uses *<em>tmp</em> as heap-allocated temporary space.
+Returns 1 if it succeeds or 0 (and sets errno) if it fails.
+</p>
+
+<p>
+<code> int hiercopy (char const *src, char const *dst) </code> <br />
+Same as above, using the <tt>satmp</tt> global stralloc as
+heap-allocated temporary space.
+</p>
+
 <h3> Variable length wrappers around Single Unix calls </h3>
 
 <p>
diff --git a/doc/upgrade.html b/doc/upgrade.html
index 74215bf..77217d9 100644
--- a/doc/upgrade.html
+++ b/doc/upgrade.html
@@ -22,6 +22,7 @@
 <ul>
  <li> wait_status() now returns 256 + signal number when WIFSIGNALED(). </li>
  <li> new macro wait_estatus() to get WIFSIGNALED information in an exit code. </li>
+ <li> new functions in djbunix.h: hiercopy(), hiercopy_tmp() </li>
 </ul>
 
 <h2> in 2.3.0.1 </h2>
diff --git a/package/deps.mak b/package/deps.mak
index 50d15b7..7f57b7c 100644
--- a/package/deps.mak
+++ b/package/deps.mak
@@ -339,6 +339,8 @@ src/libstddjb/genwrite_stdout.o src/libstddjb/genwrite_stdout.lo: src/libstddjb/
 src/libstddjb/getlnmax.o src/libstddjb/getlnmax.lo: src/libstddjb/getlnmax.c src/include/skalibs/buffer.h src/include/skalibs/bytestr.h src/include/skalibs/skamisc.h
 src/libstddjb/getlnmaxsep.o src/libstddjb/getlnmaxsep.lo: src/libstddjb/getlnmaxsep.c src/include/skalibs/buffer.h src/include/skalibs/bytestr.h src/include/skalibs/skamisc.h
 src/libstddjb/getpeereid.o src/libstddjb/getpeereid.lo: src/libstddjb/getpeereid.c src/include/skalibs/getpeereid.h src/include/skalibs/nonposix.h src/include/skalibs/sysdeps.h
+src/libstddjb/hiercopy.o src/libstddjb/hiercopy.lo: src/libstddjb/hiercopy.c src/include/skalibs/djbunix.h src/include/skalibs/skamisc.h
+src/libstddjb/hiercopy_tmp.o src/libstddjb/hiercopy_tmp.lo: src/libstddjb/hiercopy_tmp.c src/include/skalibs/bytestr.h src/include/skalibs/direntry.h src/include/skalibs/djbunix.h src/include/skalibs/stralloc.h src/include/skalibs/strerr2.h
 src/libstddjb/int_scan.o src/libstddjb/int_scan.lo: src/libstddjb/int_scan.c src/libstddjb/fmtscan-internal.h src/include/skalibs/uint.h
 src/libstddjb/iobuffer_fill.o src/libstddjb/iobuffer_fill.lo: src/libstddjb/iobuffer_fill.c src/include/skalibs/iobuffer.h
 src/libstddjb/iobuffer_flush.o src/libstddjb/iobuffer_flush.lo: src/libstddjb/iobuffer_flush.c src/include/skalibs/iobuffer.h
diff --git a/src/include/skalibs/djbunix.h b/src/include/skalibs/djbunix.h
index 6bb7de8..5e1348c 100644
--- a/src/include/skalibs/djbunix.h
+++ b/src/include/skalibs/djbunix.h
@@ -142,6 +142,9 @@ extern int rm_rf_in_tmp (stralloc *, unsigned int) ; /* caution ! */
 extern int rmstar (char const *) ;
 extern int rmstar_tmp (char const *, stralloc *) ;
 
+extern int hiercopy (char const *, char const *) ;
+extern int hiercopy_tmp (char const *, char const *, stralloc *) ;
+
 
  /* Simple spawn functions with 0 or 1 communicating fds. */
 
diff --git a/src/libstddjb/hiercopy.c b/src/libstddjb/hiercopy.c
new file mode 100644
index 0000000..34f027e
--- /dev/null
+++ b/src/libstddjb/hiercopy.c
@@ -0,0 +1,9 @@
+/* ISC license. */
+
+#include <skalibs/djbunix.h>
+#include <skalibs/skamisc.h>
+
+int hiercopy (char const *src, char const *dst)
+{
+  return hiercopy_tmp(src, dst, &satmp) ;
+}
diff --git a/src/libstddjb/hiercopy_tmp.c b/src/libstddjb/hiercopy_tmp.c
new file mode 100644
index 0000000..ce2282f
--- /dev/null
+++ b/src/libstddjb/hiercopy_tmp.c
@@ -0,0 +1,145 @@
+/* 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 filecopy (char const *src, char const *dst, mode_t mode)
+{
+  int d ;
+  int s = open_readb(src) ;
+  if (s < 0) return 0 ;
+  d = open3(dst, O_WRONLY | O_CREAT | O_TRUNC, mode) ;
+  if (d < 0)
+  {
+    fd_close(s) ;
+    return 0 ;
+  }
+  if (fd_cat(s, d) < 0) goto err ;
+  fd_close(s) ;
+  fd_close(d) ;
+  return 1 ;
+
+err:
+  {
+    register int e = errno ;
+    fd_close(s) ;
+    fd_close(d) ;
+    errno = e ;
+  }
+  return 0 ;
+}
+
+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(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 ;
+}