1 /*
2     clsync - file tree sync utility based on inotify
3 
4     Copyright (C) 2013  Dmitry Yu Okunev <dyokunev@ut.mephi.ru> 0x8E30679C
5 
6     This program is free software: you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation, either version 3 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "common.h"
21 
22 #include "port-hacks.h"
23 
24 #include "error.h"
25 #include "malloc.h"
26 
27 
fd2fpath_malloc(int fd)28 char *fd2fpath_malloc(int fd) {
29 #if __linux__
30 	stat64_t st64;
31 
32 	if(fd <= 0) {
33 		error("Invalid file descriptor supplied: fd2fpath_malloc(%i).", fd);
34 		errno = EINVAL;
35 		return NULL;
36 	}
37 
38 	char *fpath = xmalloc((1<<8) + 2);
39 	sprintf(fpath, "/proc/self/fd/%i", fd);
40 
41 	if(lstat64(fpath, &st64)) {
42 		error("Cannot lstat64(\"%s\", st64).", fpath);
43 		return NULL;
44 	}
45 
46 	ssize_t fpathlen = st64.st_size;
47 
48 	if(fpathlen > (1<<8))
49 		fpath = xrealloc(fpath, fpathlen+2);
50 
51 	debug(3, "Getting file path from symlink \"%s\". Path length is: %i.", fpath, fpathlen);
52 	if((fpathlen = readlink(fpath, fpath, fpathlen+1)) < 0) {
53 		error("Cannot readlink(\"%s\", fpath, bufsize).", fpath);
54 		return NULL;
55 	}
56 	debug(3, "The path is: \"%s\"", fpath);
57 
58 	fpath[fpathlen] = 0;
59 	return fpath;
60 #else
61 	critical("Function fd2fpath_malloc() is not supported in this OS");
62 	return NULL;
63 #endif
64 }
65 
66 /**
67  * @brief 			Copies file
68  *
69  * @param[in] 	path_from 	Source file path
70  * @param[in] 	path_to		Destination file path
71  *
72  * @retval	zero 		Successfully copied
73  * @retval	non-zero 	Got error, while copying
74  *
75  */
76 
fileutils_copy(const char * path_from,const char * path_to)77 int fileutils_copy(const char *path_from, const char *path_to) {
78 	char buf[BUFSIZ];
79 	FILE *from, *to;
80 
81 	from = fopen(path_from, "r");
82 	if(from == NULL) {
83 		error("fileutils_copy(\"%s\", \"%s\"): Cannot open file \"%s\" for reading",
84 			path_from, path_to, path_from);
85 		return errno;
86 	}
87 
88 	to   = fopen(path_to,   "w");
89 	if(to == NULL) {
90 		error("fileutils_copy(\"%s\", \"%s\"): Cannot open file \"%s\" for writing",
91 			path_from, path_to, path_to);
92 		return errno;
93 	}
94 
95 	while(!feof(from)) {
96 		int err;
97 		size_t r, w;
98 
99 		r =  fread(buf, 1, BUFSIZ, from);
100 		if((err=ferror(from))) {
101 			error("fileutils_copy(\"%s\", \"%s\"): Cannot read from file \"%s\"",
102 				path_from, path_to, path_from);
103 			return errno;	// CHECK: Is the "errno" should be used in fread() case?
104 		}
105 
106 		w = fwrite(buf, 1, r,      to);
107 		if((err=ferror(to))) {
108 			error("fileutils_copy(\"%s\", \"%s\"): Cannot write to file \"%s\"",
109 				path_from, path_to, path_to);
110 			return errno;	// CHECK: is the "errno" should be used in fwrite() case?
111 		}
112 		if(r != w) {
113 			error("fileutils_copy(\"%s\", \"%s\"): Got error while writing to file \"%s\" (%u != %u)",
114 				path_from, path_to, path_to, r, w);
115 			return errno;	// CHECK: is the "errno" should be used in case "r != w"?
116 		}
117 	}
118 
119 	return 0;
120 }
121 
122 
123 /**
124  * @brief 				Calculates directory level of a canonized path (actually it just counts "/"-s)
125  *
126  * @param[in] 	path 			Canonized path (with realpath())
127  *
128  * @retval	zero or prositive	Direcory level
129  * @retval	negative 		Got error, while calculation. Error-code is placed to errno.
130  *
131  */
132 
fileutils_calcdirlevel(const char * path)133 short int fileutils_calcdirlevel(const char *path) {
134 	short int dirlevel = 0;
135 	const char *ptr = path;
136 
137 	if(path == NULL) {
138 		error("path is NULL.");
139 		errno=EINVAL;
140 		return -1;
141 	}
142 
143 	if(*path == 0) {
144 		error("path has zero length.");
145 		errno=EINVAL;
146 		return -2;
147 	}
148 
149 	if(*path != '/') {
150 		error("path \"%s\" is not canonized.", path);
151 		errno=EINVAL;
152 		return -3;
153 	}
154 
155 	while(*(ptr++))
156 		if(*ptr == '/')
157 			dirlevel++;
158 
159 	return dirlevel;
160 }
161 
162 /**
163  * @brief 			Combination of mkdirat() and openat()
164  *
165  * @param[in]	dir_path	Path to directory to create and open
166  @ @param[in]	dirfd_parent	File descriptor of directory for relative paths
167  @ @param[in]	dir_mode	Modes for newly created directory (e.g. 750)
168  *
169  * @retval	dirfd		File descriptor to newly created directory
170  * @retval	NULL		On error
171  *
172  */
mkdirat_open(const char * const dir_path,int dirfd_parent,mode_t dir_mode)173 int mkdirat_open(const char *const dir_path, int dirfd_parent, mode_t dir_mode) {
174 	int dirfd;
175 
176 	debug(5, "mkdirat(%u, \"%s\", %o)", dirfd_parent, dir_path, dir_mode);
177 	if (mkdirat(dirfd_parent, dir_path, dir_mode))
178 		return -1;
179 
180 	debug(5, "openat(%u, \"%s\", %x)", dirfd_parent, dir_path, O_RDWR|O_DIRECTORY|O_PATH);
181 	dirfd = openat(dirfd_parent, dir_path, O_RDWR|O_DIRECTORY|O_PATH);
182 	if (dirfd == -1)
183 		return -1;
184 
185 	return dirfd;
186 }
187 
188