Add functions for safe renaming and copying of files
* libmailutils/base/copyfile.c: New file. * libmailutils/base/renamefile.c: New file. * include/mailutils/util.h (mu_copy_file) (mu_rename_file): New protos. * libmailutils/base/Makefile.am: Add new files. * examples/rename.c: New file. * examples/Makefile.am: Add new files. * NEWS: Update.
Showing
6 changed files
with
467 additions
and
0 deletions
examples/rename.c
0 → 100644
1 | #include <mailutils/mailutils.h> | ||
2 | |||
3 | int copy_option; | ||
4 | int owner_option; | ||
5 | int mode_option; | ||
6 | |||
7 | static struct mu_option rename_options[] = { | ||
8 | { "copy", 'c', NULL, MU_OPTION_DEFAULT, | ||
9 | "copy the file", | ||
10 | mu_c_bool, ©_option }, | ||
11 | { "owner", 'u', NULL, MU_OPTION_DEFAULT, | ||
12 | "copy ownership", | ||
13 | mu_c_bool, &owner_option }, | ||
14 | { "mode", 'm', NULL, MU_OPTION_DEFAULT, | ||
15 | "copy mode", | ||
16 | mu_c_bool, &mode_option }, | ||
17 | MU_OPTION_END | ||
18 | }, *options[] = { rename_options, NULL }; | ||
19 | |||
20 | struct mu_cli_setup cli = { | ||
21 | options, | ||
22 | NULL, | ||
23 | "copy or rename file", | ||
24 | "SRC DST" | ||
25 | }; | ||
26 | |||
27 | static char *capa[] = { | ||
28 | "debug", | ||
29 | NULL | ||
30 | }; | ||
31 | |||
32 | int | ||
33 | main (int argc, char **argv) | ||
34 | { | ||
35 | int rc; | ||
36 | |||
37 | mu_cli (argc, argv, &cli, capa, NULL, &argc, &argv); | ||
38 | |||
39 | if (argc != 2) | ||
40 | { | ||
41 | mu_error ("wrong number of arguments"); | ||
42 | return 1; | ||
43 | } | ||
44 | |||
45 | if (copy_option) | ||
46 | { | ||
47 | int flags = (owner_option ? MU_COPY_OWNER : 0) | ||
48 | | (mode_option ? MU_COPY_MODE : 0); | ||
49 | rc = mu_copy_file (argv[0], argv[1], flags); | ||
50 | } | ||
51 | else | ||
52 | rc = mu_rename_file (argv[0], argv[1]); | ||
53 | |||
54 | if (rc) | ||
55 | mu_diag_funcall (MU_DIAG_ERROR, "mu_rename_file", NULL, rc); | ||
56 | |||
57 | return !!rc; | ||
58 | } | ||
59 | |||
60 |
... | @@ -204,6 +204,17 @@ extern char const *mu_c_type_str[]; | ... | @@ -204,6 +204,17 @@ extern char const *mu_c_type_str[]; |
204 | int mu_str_to_c (char const *string, mu_c_type_t type, void *tgt, | 204 | int mu_str_to_c (char const *string, mu_c_type_t type, void *tgt, |
205 | char **errmsg); | 205 | char **errmsg); |
206 | 206 | ||
207 | /* -------------------------- */ | ||
208 | /* Safe file copy and rename */ | ||
209 | /* -------------------------- */ | ||
210 | #define MU_COPY_MODE 0x01 | ||
211 | #define MU_COPY_OWNER 0x02 | ||
212 | #define MU_COPY_SYMLINK 0x04 | ||
213 | #define MU_COPY_FORCE 0x08 | ||
214 | |||
215 | int mu_copy_file (const char *srcpath, const char *dstpath, int flags); | ||
216 | int mu_rename_file (const char *oldpath, const char *newpath); | ||
217 | |||
207 | /* ----------------------- */ | 218 | /* ----------------------- */ |
208 | /* Assorted functions. */ | 219 | /* Assorted functions. */ |
209 | /* ----------------------- */ | 220 | /* ----------------------- */ | ... | ... |
... | @@ -24,6 +24,7 @@ libbase_la_SOURCES = \ | ... | @@ -24,6 +24,7 @@ libbase_la_SOURCES = \ |
24 | argcvjoin.c\ | 24 | argcvjoin.c\ |
25 | argcvrem.c\ | 25 | argcvrem.c\ |
26 | assoc.c\ | 26 | assoc.c\ |
27 | copyfile.c\ | ||
27 | ctparse.c\ | 28 | ctparse.c\ |
28 | daemon.c\ | 29 | daemon.c\ |
29 | filesafety.c\ | 30 | filesafety.c\ |
... | @@ -57,6 +58,7 @@ libbase_la_SOURCES = \ | ... | @@ -57,6 +58,7 @@ libbase_la_SOURCES = \ |
57 | permstr.c\ | 58 | permstr.c\ |
58 | registrar.c\ | 59 | registrar.c\ |
59 | refcount.c\ | 60 | refcount.c\ |
61 | renamefile.c\ | ||
60 | rfc2047.c\ | 62 | rfc2047.c\ |
61 | schemeauto.c\ | 63 | schemeauto.c\ |
62 | sha1.c\ | 64 | sha1.c\ | ... | ... |
libmailutils/base/copyfile.c
0 → 100644
1 | #include <config.h> | ||
2 | #include <sys/types.h> | ||
3 | #include <sys/stat.h> | ||
4 | #include <dirent.h> | ||
5 | #include <stdlib.h> | ||
6 | #include <unistd.h> | ||
7 | #include <mailutils/stream.h> | ||
8 | #include <mailutils/util.h> | ||
9 | #include <mailutils/diag.h> | ||
10 | #include <mailutils/error.h> | ||
11 | #include <mailutils/errno.h> | ||
12 | #include <mailutils/nls.h> | ||
13 | |||
14 | static int copy_regular_file (const char *srcpath, const char *dstpath, | ||
15 | int flags, struct stat *st); | ||
16 | static int copy_symlink (const char *srcpath, const char *dstpath); | ||
17 | static int copy_dir (const char *srcpath, const char *dstpath, int flags); | ||
18 | |||
19 | int | ||
20 | mu_copy_file (const char *srcpath, const char *dstpath, int flags) | ||
21 | { | ||
22 | int rc = 0; | ||
23 | struct stat st; | ||
24 | |||
25 | if (((flags & MU_COPY_SYMLINK) ? lstat : stat) (srcpath, &st)) | ||
26 | { | ||
27 | mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, | ||
28 | (_("can't stat file %s: %s"), | ||
29 | srcpath, mu_strerror (errno))); | ||
30 | return errno; | ||
31 | } | ||
32 | |||
33 | switch (st.st_mode & S_IFMT) | ||
34 | { | ||
35 | case S_IFREG: | ||
36 | return copy_regular_file (srcpath, dstpath, flags, &st); | ||
37 | |||
38 | case S_IFLNK: | ||
39 | return copy_symlink (srcpath, dstpath); | ||
40 | break; | ||
41 | |||
42 | case S_IFDIR: | ||
43 | return copy_dir (srcpath, dstpath, flags); | ||
44 | break; | ||
45 | |||
46 | case S_IFBLK: | ||
47 | case S_IFCHR: | ||
48 | if (mknod (dstpath, st.st_mode & 0777, st.st_dev)) | ||
49 | { | ||
50 | rc = errno; | ||
51 | mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, | ||
52 | (_("%s: cannot create node: %s"), | ||
53 | dstpath, | ||
54 | mu_strerror (rc))); | ||
55 | } | ||
56 | break; | ||
57 | |||
58 | case S_IFIFO: | ||
59 | if (mkfifo (dstpath, st.st_mode & 0777)) | ||
60 | { | ||
61 | rc = errno; | ||
62 | mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, | ||
63 | (_("%s: cannot create node: %s"), | ||
64 | dstpath, | ||
65 | mu_strerror (rc))); | ||
66 | } | ||
67 | break; | ||
68 | |||
69 | default: | ||
70 | mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, | ||
71 | (_("%s: don't know how to copy file of that type"), | ||
72 | srcpath)); | ||
73 | return ENOTSUP; | ||
74 | } | ||
75 | |||
76 | return rc; | ||
77 | } | ||
78 | |||
79 | static int | ||
80 | copy_regular_file (const char *srcpath, const char *dstpath, int flags, | ||
81 | struct stat *st) | ||
82 | { | ||
83 | int rc; | ||
84 | mu_stream_t src, dst; | ||
85 | mode_t mask, mode; | ||
86 | |||
87 | rc = mu_file_stream_create (&src, srcpath, MU_STREAM_READ); | ||
88 | if (rc) | ||
89 | { | ||
90 | mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, | ||
91 | (_("cannot open source file %s: %s"), | ||
92 | srcpath, mu_strerror (rc))); | ||
93 | return rc; | ||
94 | } | ||
95 | |||
96 | mask = umask (077); | ||
97 | mode = ((flags & MU_COPY_MODE) ? st->st_mode : (0666 & ~mask)) & 0777; | ||
98 | |||
99 | rc = mu_file_stream_create (&dst, dstpath, MU_STREAM_CREAT|MU_STREAM_WRITE); | ||
100 | umask (mask); | ||
101 | if (rc) | ||
102 | { | ||
103 | mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, | ||
104 | (_("cannot open destination file %s: %s"), | ||
105 | dstpath, mu_strerror (rc))); | ||
106 | mu_stream_destroy (&src); | ||
107 | return rc; | ||
108 | } | ||
109 | |||
110 | rc = mu_stream_copy (dst, src, 0, NULL); | ||
111 | if (rc) | ||
112 | { | ||
113 | mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, | ||
114 | (_("failed to copy %s to %s: %s"), | ||
115 | srcpath, dstpath, mu_strerror (rc))); | ||
116 | } | ||
117 | else | ||
118 | { | ||
119 | mu_transport_t trans[2]; | ||
120 | |||
121 | rc = mu_stream_ioctl (dst, MU_IOCTL_TRANSPORT, MU_IOCTL_OP_GET, trans); | ||
122 | if (rc == 0) | ||
123 | { | ||
124 | if (fchmod ((int) trans[0], mode)) | ||
125 | { | ||
126 | rc = errno; | ||
127 | mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, | ||
128 | (_("%s: cannot chmod: %s"), | ||
129 | dstpath, mu_strerror (rc))); | ||
130 | } | ||
131 | else if (flags & MU_COPY_OWNER) | ||
132 | { | ||
133 | uid_t uid; | ||
134 | gid_t gid; | ||
135 | |||
136 | if (getuid () == 0) | ||
137 | { | ||
138 | uid = st->st_uid; | ||
139 | gid = st->st_gid; | ||
140 | } | ||
141 | else if (getuid () == st->st_uid) | ||
142 | { | ||
143 | uid = -1; | ||
144 | gid = st->st_gid; | ||
145 | } | ||
146 | else | ||
147 | { | ||
148 | uid = -1; | ||
149 | gid = -1; | ||
150 | } | ||
151 | |||
152 | if (gid != -1) | ||
153 | { | ||
154 | if (fchown ((int) trans[0], uid, gid)) | ||
155 | { | ||
156 | rc = errno; | ||
157 | mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, | ||
158 | (_("%s: cannot chown to %lu.%lu: %s"), | ||
159 | dstpath, | ||
160 | (unsigned long) uid, | ||
161 | (unsigned long) gid, | ||
162 | mu_strerror (rc))); | ||
163 | } | ||
164 | } | ||
165 | } | ||
166 | } | ||
167 | else | ||
168 | { | ||
169 | mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, | ||
170 | (_("can't change file mode and ownership after copying %s to %s;" | ||
171 | " cannot get file handle: %s"), | ||
172 | srcpath, dstpath, | ||
173 | mu_strerror (rc))); | ||
174 | } | ||
175 | } | ||
176 | |||
177 | mu_stream_destroy (&src); | ||
178 | mu_stream_destroy (&dst); | ||
179 | |||
180 | return rc; | ||
181 | } | ||
182 | |||
183 | static int | ||
184 | copy_symlink (const char *srcpath, const char *dstpath) | ||
185 | { | ||
186 | int rc; | ||
187 | char *buf = NULL; | ||
188 | size_t size = 0; | ||
189 | |||
190 | rc = mu_readlink (srcpath, &buf, &size, NULL); | ||
191 | if (rc) | ||
192 | { | ||
193 | mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, | ||
194 | (_("%s: cannot read link: %s"), | ||
195 | srcpath, mu_strerror (rc))); | ||
196 | return rc; | ||
197 | } | ||
198 | |||
199 | if (symlink (buf, dstpath)) | ||
200 | { | ||
201 | rc = errno; | ||
202 | mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, | ||
203 | (_("%s: can't link %s to %s: %s"), | ||
204 | srcpath, buf, dstpath, mu_strerror (rc))); | ||
205 | } | ||
206 | free (buf); | ||
207 | return rc; | ||
208 | } | ||
209 | |||
210 | static int | ||
211 | copy_dir (const char *srcpath, const char *dstpath, int flags) | ||
212 | { | ||
213 | DIR *dirp; | ||
214 | struct dirent *dp; | ||
215 | struct stat st, st1; | ||
216 | int rc; | ||
217 | int create = 0; | ||
218 | mode_t mode, mask; | ||
219 | |||
220 | if (stat (srcpath, &st)) | ||
221 | { | ||
222 | mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, | ||
223 | (_("can't stat file %s: %s"), | ||
224 | srcpath, mu_strerror (errno))); | ||
225 | return errno; | ||
226 | } | ||
227 | |||
228 | if (stat (dstpath, &st1)) | ||
229 | { | ||
230 | if (errno == ENOENT) | ||
231 | create = 1; | ||
232 | else | ||
233 | { | ||
234 | mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, | ||
235 | (_("can't stat directory %s: %s"), | ||
236 | dstpath, mu_strerror (errno))); | ||
237 | return errno; | ||
238 | } | ||
239 | } | ||
240 | else if (!S_ISDIR (st1.st_mode)) | ||
241 | { | ||
242 | if (flags & MU_COPY_FORCE) | ||
243 | { | ||
244 | if (unlink (dstpath)) | ||
245 | { | ||
246 | rc = errno; | ||
247 | mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, | ||
248 | (_("%s is not a directory and cannot be unlinked: %s"), | ||
249 | dstpath, mu_strerror (rc))); | ||
250 | return rc; | ||
251 | } | ||
252 | create = 1; | ||
253 | } | ||
254 | else | ||
255 | { | ||
256 | mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, | ||
257 | (_("%s is not a directory"), | ||
258 | dstpath)); | ||
259 | return EEXIST; | ||
260 | } | ||
261 | } | ||
262 | |||
263 | mask = umask (077); | ||
264 | mode = ((flags & MU_COPY_MODE) ? st.st_mode : (0777 & ~mask)) & 0777; | ||
265 | |||
266 | if (create) | ||
267 | { | ||
268 | rc = mkdir (dstpath, 0700); | ||
269 | umask (mask); | ||
270 | |||
271 | if (rc) | ||
272 | { | ||
273 | mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, | ||
274 | (_("can't create directory %s: %s"), | ||
275 | dstpath, mu_strerror (errno))); | ||
276 | return errno; | ||
277 | } | ||
278 | } | ||
279 | else | ||
280 | umask (mask); | ||
281 | |||
282 | dirp = opendir (srcpath); | ||
283 | if (dirp == NULL) | ||
284 | { | ||
285 | mu_debug (MU_DEBCAT_FOLDER, MU_DEBUG_ERROR, | ||
286 | ("cannot open directory %s: %s", | ||
287 | srcpath, mu_strerror (errno))); | ||
288 | return 1; | ||
289 | } | ||
290 | |||
291 | while ((dp = readdir (dirp))) | ||
292 | { | ||
293 | char const *ename = dp->d_name; | ||
294 | char *src, *dst; | ||
295 | |||
296 | if (ename[ename[0] != '.' ? 0 : ename[1] != '.' ? 1 : 2] == 0) | ||
297 | continue; | ||
298 | |||
299 | src = mu_make_file_name (srcpath, ename); | ||
300 | dst = mu_make_file_name (dstpath, ename); | ||
301 | rc = mu_copy_file (src, dst, flags); | ||
302 | free (dst); | ||
303 | free (src); | ||
304 | |||
305 | if (rc) | ||
306 | break; | ||
307 | } | ||
308 | closedir (dirp); | ||
309 | |||
310 | if (chmod (dstpath, mode)) | ||
311 | { | ||
312 | rc = errno; | ||
313 | mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, | ||
314 | (_("%s: cannot chmod: %s"), | ||
315 | dstpath, mu_strerror (rc))); | ||
316 | } | ||
317 | else if (flags & MU_COPY_OWNER) | ||
318 | { | ||
319 | uid_t uid; | ||
320 | gid_t gid; | ||
321 | |||
322 | if (getuid () == 0) | ||
323 | { | ||
324 | uid = st.st_uid; | ||
325 | gid = st.st_gid; | ||
326 | } | ||
327 | else if (getuid () == st.st_uid) | ||
328 | { | ||
329 | uid = -1; | ||
330 | gid = st.st_gid; | ||
331 | } | ||
332 | else | ||
333 | { | ||
334 | uid = -1; | ||
335 | gid = -1; | ||
336 | } | ||
337 | |||
338 | if (gid != -1) | ||
339 | { | ||
340 | if (chown (dstpath, uid, gid)) | ||
341 | { | ||
342 | rc = errno; | ||
343 | mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, | ||
344 | (_("%s: cannot chown to %lu.%lu: %s"), | ||
345 | dstpath, | ||
346 | (unsigned long) uid, | ||
347 | (unsigned long) gid, | ||
348 | mu_strerror (rc))); | ||
349 | } | ||
350 | } | ||
351 | } | ||
352 | return rc; | ||
353 | } | ||
354 |
libmailutils/base/renamefile.c
0 → 100644
1 | #include <config.h> | ||
2 | #include <stdio.h> | ||
3 | #include <unistd.h> | ||
4 | #include <mailutils/stream.h> | ||
5 | #include <mailutils/util.h> | ||
6 | #include <mailutils/diag.h> | ||
7 | #include <mailutils/error.h> | ||
8 | #include <mailutils/errno.h> | ||
9 | #include <mailutils/nls.h> | ||
10 | |||
11 | int | ||
12 | mu_rename_file (const char *oldpath, const char *newpath) | ||
13 | { | ||
14 | int rc; | ||
15 | |||
16 | if (rename (oldpath, newpath) == 0) | ||
17 | return 0; | ||
18 | |||
19 | if (errno == EXDEV) | ||
20 | { | ||
21 | mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, | ||
22 | (_("cannot rename %s to %s: %s"), | ||
23 | oldpath, newpath, mu_strerror (errno))); | ||
24 | mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_TRACE1, | ||
25 | (_("attempting copy"))); | ||
26 | |||
27 | rc = mu_copy_file (oldpath, newpath, MU_COPY_MODE|MU_COPY_OWNER); | ||
28 | if (rc == 0) | ||
29 | { | ||
30 | if (unlink (oldpath)) | ||
31 | { | ||
32 | mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, | ||
33 | (_("copied %s to %s, but failed to remove the source: %s"), | ||
34 | oldpath, newpath, mu_strerror (errno))); | ||
35 | } | ||
36 | } | ||
37 | } | ||
38 | return rc; | ||
39 | } |
-
Please register or sign in to post a comment