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 (cp_aflag && (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) ||
104	           S_ISSOCK(st.st_mode) || S_ISFIFO(st.st_mode))) {
105		if (cp_fflag && unlink(s2) < 0 && errno != ENOENT) {
106			weprintf("unlink %s:", s2);
107			cp_status = 1;
108			return 0;
109		} else if (mknod(s2, st.st_mode, st.st_rdev) < 0) {
110			weprintf("mknod %s:", s2);
111			cp_status = 1;
112			return 0;
113		}
114	} else {
115		if ((f1 = open(s1, O_RDONLY)) < 0) {
116			weprintf("open %s:", s1);
117			cp_status = 1;
118			return 0;
119		}
120		if ((f2 = creat(s2, st.st_mode)) < 0 && cp_fflag) {
121			if (unlink(s2) < 0 && errno != ENOENT) {
122				weprintf("unlink %s:", s2);
123				cp_status = 1;
124				close(f1);
125				return 0;
126			}
127			f2 = creat(s2, st.st_mode);
128		}
129		if (f2 < 0) {
130			weprintf("creat %s:", s2);
131			cp_status = 1;
132			close(f1);
133			return 0;
134		}
135		if (concat(f1, s1, f2, s2) < 0) {
136			cp_status = 1;
137			close(f1);
138			close(f2);
139			return 0;
140		}
141
142		close(f1);
143		close(f2);
144	}
145
146	if (cp_aflag || cp_pflag) {
147		/* atime and mtime */
148		times[0] = st.st_atim;
149		times[1] = st.st_mtim;
150		if (utimensat(AT_FDCWD, s2, times, AT_SYMLINK_NOFOLLOW) < 0) {
151			weprintf("utimensat %s:", s2);
152			cp_status = 1;
153		}
154
155		/* owner and mode */
156		if (!S_ISLNK(st.st_mode)) {
157			if (chown(s2, st.st_uid, st.st_gid) < 0) {
158				weprintf("chown %s:", s2);
159				cp_status = 1;
160				st.st_mode &= ~(S_ISUID | S_ISGID);
161			}
162			if (chmod(s2, st.st_mode) < 0) {
163				weprintf("chmod %s:", s2);
164				cp_status = 1;
165			}
166		} else {
167			if (lchown(s2, st.st_uid, st.st_gid) < 0) {
168				weprintf("lchown %s:", s2);
169				cp_status = 1;
170				return 0;
171			}
172		}
173	}
174
175	return 0;
176}