Commit e87f373e e87f373e24cd64b77e9b66348fc8aa0c53de5d52 by Sergey Poznyakoff

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.
1 parent 64ef4cea
...@@ -44,6 +44,7 @@ noinst_PROGRAMS = \ ...@@ -44,6 +44,7 @@ noinst_PROGRAMS = \
44 murun\ 44 murun\
45 musocio\ 45 musocio\
46 $(NNTPCLIENT)\ 46 $(NNTPCLIENT)\
47 rename\
47 sa\ 48 sa\
48 sfrom 49 sfrom
49 50
......
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, &copy_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
...@@ -203,7 +203,18 @@ typedef enum mu_c_type mu_c_type_t; ...@@ -203,7 +203,18 @@ typedef enum mu_c_type mu_c_type_t;
203 extern char const *mu_c_type_str[]; 203 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
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
206 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\
......
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
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 }