master xplshn/aruu / shared / libutil / cp.c
  1/* See LICENSE file for copyright and license details. */
  2#include <dirent.h>
  3#include <errno.h>
  4#include <fcntl.h>
  5#include <limits.h>
  6#include <stdio.h>
  7#include <stdlib.h>
  8#include <string.h>
  9#include <sys/stat.h>
 10#include <sys/types.h>
 11#include <unistd.h>
 12#include <utime.h>
 13
 14#include "../fs.h"
 15#include "../util.h"
 16
 17int cp_aflag  = 0;
 18int cp_fflag  = 0;
 19int cp_iflag  = 0;
 20int cp_pflag  = 0;
 21int cp_rflag  = 0;
 22int cp_vflag  = 0;
 23int cp_status = 0;
 24int cp_follow;
 25
 26int
 27cp(const char *s1, const char *s2, int depth)
 28{
 29  DIR            *dp;
 30  int             f1, f2, flags = 0;
 31  struct dirent  *d;
 32  struct stat     st;
 33  struct timespec times[2];
 34  ssize_t         r;
 35  char            target[PATH_MAX], ns1[PATH_MAX], ns2[PATH_MAX];
 36
 37  if (cp_follow == 'P' || (cp_follow == 'H' && depth))
 38    flags |= AT_SYMLINK_NOFOLLOW;
 39
 40  if (fstatat(AT_FDCWD, s1, &st, flags) < 0) {
 41    weprintf("stat %s:", s1);
 42    cp_status = 1;
 43    return 0;
 44  }
 45
 46  if (cp_iflag && access(s2, F_OK) == 0) {
 47    if (!confirm("overwrite '%s'? ", s2))
 48      return 0;
 49  }
 50
 51  if (cp_vflag)
 52    printf("%s -> %s\n", s1, s2);
 53
 54  if (S_ISLNK(st.st_mode)) {
 55    if ((r = readlink(s1, target, sizeof(target) - 1)) >= 0) {
 56      target[r] = '\0';
 57      if (cp_fflag && unlink(s2) < 0 && errno != ENOENT) {
 58        weprintf("unlink %s:", s2);
 59        cp_status = 1;
 60        return 0;
 61      } else if (symlink(target, s2) < 0) {
 62        weprintf("symlink %s -> %s:", s2, target);
 63        cp_status = 1;
 64        return 0;
 65      }
 66    }
 67  } else if (S_ISDIR(st.st_mode)) {
 68    if (!cp_rflag) {
 69      weprintf("%s is a directory\n", s1);
 70      cp_status = 1;
 71      return 0;
 72    }
 73    if (!(dp = opendir(s1))) {
 74      weprintf("opendir %s:", s1);
 75      cp_status = 1;
 76      return 0;
 77    }
 78    if (mkdir(s2, st.st_mode) < 0 && errno != EEXIST) {
 79      weprintf("mkdir %s:", s2);
 80      cp_status = 1;
 81      closedir(dp);
 82      return 0;
 83    }
 84
 85    while ((d = readdir(dp))) {
 86      if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
 87        continue;
 88
 89      estrlcpy(ns1, s1, sizeof(ns1));
 90      if (s1[strlen(s1) - 1] != '/')
 91        estrlcat(ns1, "/", sizeof(ns1));
 92      estrlcat(ns1, d->d_name, sizeof(ns1));
 93
 94      estrlcpy(ns2, s2, sizeof(ns2));
 95      if (s2[strlen(s2) - 1] != '/')
 96        estrlcat(ns2, "/", sizeof(ns2));
 97      estrlcat(ns2, d->d_name, sizeof(ns2));
 98
 99      fnck(ns1, ns2, cp, depth + 1);
100    }
101
102    closedir(dp);
103  } else if (
104      cp_aflag
105      && (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) || S_ISSOCK(st.st_mode)
106          || S_ISFIFO(st.st_mode))
107  ) {
108    if (cp_fflag && unlink(s2) < 0 && errno != ENOENT) {
109      weprintf("unlink %s:", s2);
110      cp_status = 1;
111      return 0;
112    } else if (mknod(s2, st.st_mode, st.st_rdev) < 0) {
113      weprintf("mknod %s:", s2);
114      cp_status = 1;
115      return 0;
116    }
117  } else {
118    if ((f1 = open(s1, O_RDONLY)) < 0) {
119      weprintf("open %s:", s1);
120      cp_status = 1;
121      return 0;
122    }
123    if ((f2 = creat(s2, st.st_mode)) < 0 && cp_fflag) {
124      if (unlink(s2) < 0 && errno != ENOENT) {
125        weprintf("unlink %s:", s2);
126        cp_status = 1;
127        close(f1);
128        return 0;
129      }
130      f2 = creat(s2, st.st_mode);
131    }
132    if (f2 < 0) {
133      weprintf("creat %s:", s2);
134      cp_status = 1;
135      close(f1);
136      return 0;
137    }
138    if (concat(f1, s1, f2, s2) < 0) {
139      cp_status = 1;
140      close(f1);
141      close(f2);
142      return 0;
143    }
144
145    close(f1);
146    close(f2);
147  }
148
149  if (cp_aflag || cp_pflag) {
150    /* atime and mtime */
151    times[0] = st.st_atim;
152    times[1] = st.st_mtim;
153    if (utimensat(AT_FDCWD, s2, times, AT_SYMLINK_NOFOLLOW) < 0) {
154      weprintf("utimensat %s:", s2);
155      cp_status = 1;
156    }
157
158    /* owner and mode */
159    if (!S_ISLNK(st.st_mode)) {
160      if (chown(s2, st.st_uid, st.st_gid) < 0) {
161        weprintf("chown %s:", s2);
162        cp_status = 1;
163        st.st_mode &= ~(S_ISUID | S_ISGID);
164      }
165      if (chmod(s2, st.st_mode) < 0) {
166        weprintf("chmod %s:", s2);
167        cp_status = 1;
168      }
169    } else {
170      if (lchown(s2, st.st_uid, st.st_gid) < 0) {
171        weprintf("lchown %s:", s2);
172        cp_status = 1;
173        return 0;
174      }
175    }
176  }
177
178  return 0;
179}