1/*
2 * Syscall wrappers to ensure that nothing gets done in dry_run mode
3 * and to handle system peculiarities.
4 *
5 * Copyright (C) 1998 Andrew Tridgell
6 * Copyright (C) 2002 Martin Pool
7 * Copyright (C) 2003, 2004, 2005, 2006 Wayne Davison
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
22 */
23
24#include "rsync.h"
25
26#if !defined MKNOD_CREATES_SOCKETS && defined HAVE_SYS_UN_H
27#include <sys/un.h>
28#endif
29
30#ifdef HAVE_COPYFILE_H
31#include <libgen.h>
32#include <copyfile.h>
33#endif
34
35extern int dry_run;
36extern int read_only;
37extern int list_only;
38extern int preserve_perms;
39#ifdef EA_SUPPORT
40extern int extended_attributes;
41extern int preserve_links;
42#endif
43
44#define RETURN_ERROR_IF(x,e) \
45	do { \
46		if (x) { \
47			errno = (e); \
48			return -1; \
49		} \
50	} while (0)
51
52#define RETURN_ERROR_IF_RO_OR_LO RETURN_ERROR_IF(read_only || list_only, EROFS)
53
54int do_unlink(const char *fname)
55{
56	if (dry_run) return 0;
57	RETURN_ERROR_IF_RO_OR_LO;
58#ifdef HAVE_COPYFILE
59	if (extended_attributes && !strncmp("._", basename(fname), 2))
60	{
61	    int ret;
62	    ret = unlink(fname);
63
64	    if(ret == -1 && errno != ENOENT)
65		return -1;
66	    else
67		return 0;
68	}
69#endif
70	return unlink(fname);
71}
72
73int do_symlink(const char *fname1, const char *fname2)
74{
75	if (dry_run) return 0;
76	RETURN_ERROR_IF_RO_OR_LO;
77	return symlink(fname1, fname2);
78}
79
80#ifdef HAVE_LINK
81int do_link(const char *fname1, const char *fname2)
82{
83	if (dry_run) return 0;
84	RETURN_ERROR_IF_RO_OR_LO;
85	return link(fname1, fname2);
86}
87#endif
88
89int do_lchown(const char *path, uid_t owner, gid_t group)
90{
91	if (dry_run) return 0;
92	RETURN_ERROR_IF_RO_OR_LO;
93#ifndef HAVE_LCHOWN
94#define lchown chown
95#endif
96	return lchown(path, owner, group);
97}
98
99int do_mknod(char *pathname, mode_t mode, dev_t dev)
100{
101	if (dry_run) return 0;
102	RETURN_ERROR_IF_RO_OR_LO;
103#if !defined MKNOD_CREATES_FIFOS && defined HAVE_MKFIFO
104	if (S_ISFIFO(mode))
105		return mkfifo(pathname, mode);
106#endif
107#if !defined MKNOD_CREATES_SOCKETS && defined HAVE_SYS_UN_H
108	if (S_ISSOCK(mode)) {
109		int sock;
110		struct sockaddr_un saddr;
111		unsigned int len;
112
113		saddr.sun_family = AF_UNIX;
114		len = strlcpy(saddr.sun_path, pathname, sizeof saddr.sun_path);
115#ifdef HAVE_SOCKADDR_UN_LEN
116		saddr.sun_len = len >= sizeof saddr.sun_path
117			      ? sizeof saddr.sun_path : len + 1;
118#endif
119
120		if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0
121		    || (unlink(pathname) < 0 && errno != ENOENT)
122		    || (bind(sock, (struct sockaddr*)&saddr, sizeof saddr)) < 0)
123			return -1;
124		close(sock);
125#ifdef HAVE_CHMOD
126		return do_chmod(pathname, mode);
127#else
128		return 0;
129#endif
130	}
131#endif
132#ifdef HAVE_MKNOD
133	return mknod(pathname, mode, dev);
134#else
135	return -1;
136#endif
137}
138
139int do_rmdir(const char *pathname)
140{
141	if (dry_run) return 0;
142	RETURN_ERROR_IF_RO_OR_LO;
143	return rmdir(pathname);
144}
145
146int do_open(const char *pathname, int flags, mode_t mode)
147{
148	if (flags != O_RDONLY) {
149		RETURN_ERROR_IF(dry_run, 0);
150		RETURN_ERROR_IF_RO_OR_LO;
151	}
152
153	return open(pathname, flags | O_BINARY, mode);
154}
155
156#ifdef HAVE_CHMOD
157int do_chmod(const char *path, mode_t mode)
158{
159	int code;
160	if (dry_run) return 0;
161	RETURN_ERROR_IF_RO_OR_LO;
162	if (S_ISLNK(mode)) {
163#ifdef HAVE_LCHMOD
164		code = lchmod(path, mode & CHMOD_BITS);
165#else
166		code = 1;
167#endif
168	} else
169		code = chmod(path, mode & CHMOD_BITS);
170	if (code != 0 && preserve_perms)
171	    return code;
172	return 0;
173}
174#endif
175
176int do_rename(const char *fname1, const char *fname2)
177{
178	if (dry_run) return 0;
179	RETURN_ERROR_IF_RO_OR_LO;
180#ifdef HAVE_COPYFILE
181	if(extended_attributes)
182	{
183		char dst_fname[MAXPATHLEN];
184		if(!strncmp(basename(fname1), ".._", 3))
185		{
186			snprintf(dst_fname, MAXPATHLEN, "%s/%s", dirname(fname2), basename(fname2) + 2);
187			if(copyfile(fname1, dst_fname, 0,
188				    COPYFILE_UNPACK | COPYFILE_ACL | COPYFILE_XATTR | (preserve_links ? COPYFILE_NOFOLLOW : 0)) == 0)
189				return unlink(fname1);
190			else
191				rprintf(FERROR,"copyfile(%s,%s, COPYFILE_UNPACK) failed:%d\n", fname1, dst_fname, errno);
192
193		}
194	}
195#endif
196	return rename(fname1, fname2);
197}
198
199void trim_trailing_slashes(char *name)
200{
201	int l;
202	/* Some BSD systems cannot make a directory if the name
203	 * contains a trailing slash.
204	 * <http://www.opensource.apple.com/bugs/X/BSD%20Kernel/2734739.html> */
205
206	/* Don't change empty string; and also we can't improve on
207	 * "/" */
208
209	l = strlen(name);
210	while (l > 1) {
211		if (name[--l] != '/')
212			break;
213		name[l] = '\0';
214	}
215}
216
217int do_mkdir(char *fname, mode_t mode)
218{
219	if (dry_run) return 0;
220	RETURN_ERROR_IF_RO_OR_LO;
221	trim_trailing_slashes(fname);
222	return mkdir(fname, mode);
223}
224
225/* like mkstemp but forces permissions */
226int do_mkstemp(char *template, mode_t perms)
227{
228	RETURN_ERROR_IF(dry_run, 0);
229	RETURN_ERROR_IF(read_only, EROFS);
230
231#if defined HAVE_SECURE_MKSTEMP && defined HAVE_FCHMOD && (!defined HAVE_OPEN64 || defined HAVE_MKSTEMP64)
232	{
233		int fd = mkstemp(template);
234		if (fd == -1)
235			return -1;
236		if (fchmod(fd, perms) != 0 && preserve_perms) {
237			int errno_save = errno;
238			close(fd);
239			unlink(template);
240			errno = errno_save;
241			return -1;
242		}
243#if defined HAVE_SETMODE && O_BINARY
244		setmode(fd, O_BINARY);
245#endif
246		return fd;
247	}
248#else
249	if (!mktemp(template))
250		return -1;
251	return do_open(template, O_RDWR|O_EXCL|O_CREAT, perms);
252#endif
253}
254
255int do_stat(const char *fname, STRUCT_STAT *st)
256{
257#ifdef USE_STAT64_FUNCS
258	return stat64(fname, st);
259#else
260	return stat(fname, st);
261#endif
262}
263
264int do_lstat(const char *fname, STRUCT_STAT *st)
265{
266#ifdef SUPPORT_LINKS
267# ifdef USE_STAT64_FUNCS
268	return lstat64(fname, st);
269# else
270	return lstat(fname, st);
271# endif
272#else
273	return do_stat(fname, st);
274#endif
275}
276
277int do_fstat(int fd, STRUCT_STAT *st)
278{
279#ifdef USE_STAT64_FUNCS
280	return fstat64(fd, st);
281#else
282	return fstat(fd, st);
283#endif
284}
285
286OFF_T do_lseek(int fd, OFF_T offset, int whence)
287{
288#ifdef HAVE_LSEEK64
289#if !SIZEOF_OFF64_T
290	OFF_T lseek64();
291#else
292	off64_t lseek64();
293#endif
294	return lseek64(fd, offset, whence);
295#else
296	return lseek(fd, offset, whence);
297#endif
298}
299
300char *d_name(struct dirent *di)
301{
302#ifdef HAVE_BROKEN_READDIR
303	return (di->d_name - 2);
304#else
305	return di->d_name;
306#endif
307}
308