source: mainline/uspace/app/bdsh/cmds/modules/cp/cp.c@ 944f8fdd

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 944f8fdd was 582a0b8, checked in by Jakub Jermar <jakub@…>, 8 years ago

Remove unistd.h

  • Rename usleep() and sleep() to thread_usleep() and thread_sleep() and move to thread.[hc].
  • Include stddef.h in order to provide NULL.
  • Move getpagesize() to libposix.
  • Sync uspace/dist/src/c/demos with originals.
  • Property mode set to 100644
File size: 12.3 KB
Line 
1/*
2 * Copyright (c) 2008 Tim Post
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <errno.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <io/console.h>
33#include <io/keycode.h>
34#include <getopt.h>
35#include <str.h>
36#include <vfs/vfs.h>
37#include <dirent.h>
38#include "config.h"
39#include "util.h"
40#include "errors.h"
41#include "entry.h"
42#include "cp.h"
43#include "cmds.h"
44
45#define CP_VERSION "0.0.1"
46#define CP_DEFAULT_BUFLEN 1024
47
48static const char *cmdname = "cp";
49static console_ctrl_t *con;
50
51static struct option const long_options[] = {
52 { "buffer", required_argument, 0, 'b' },
53 { "force", no_argument, 0, 'f' },
54 { "interactive", no_argument, 0, 'i'},
55 { "recursive", no_argument, 0, 'r' },
56 { "help", no_argument, 0, 'h' },
57 { "version", no_argument, 0, 'v' },
58 { "verbose", no_argument, 0, 'V' },
59 { 0, 0, 0, 0 }
60};
61
62typedef enum {
63 TYPE_NONE,
64 TYPE_FILE,
65 TYPE_DIR
66} dentry_type_t;
67
68static int64_t copy_file(const char *src, const char *dest,
69 size_t blen, int vb);
70
71/** Get the type of a directory entry.
72 *
73 * @param path Path of the directory entry.
74 *
75 * @return TYPE_DIR if the dentry is a directory.
76 * @return TYPE_FILE if the dentry is a file.
77 * @return TYPE_NONE if the dentry does not exists.
78 */
79static dentry_type_t get_type(const char *path)
80{
81 struct stat s;
82
83 int r = vfs_stat_path(path, &s);
84
85 if (r != EOK)
86 return TYPE_NONE;
87 else if (s.is_directory)
88 return TYPE_DIR;
89 else if (s.is_file)
90 return TYPE_FILE;
91
92 return TYPE_NONE;
93}
94
95static int strtoint(const char *s1)
96{
97 long t1;
98
99 if (-1 == (t1 = strtol(s1, (char **) NULL, 10)))
100 return -1;
101
102 if (t1 <= 0)
103 return -1;
104
105 return (int) t1;
106}
107
108/** Get the last component of a path.
109 *
110 * e.g. /data/a ---> a
111 *
112 * @param path Pointer to the path.
113 *
114 * @return Pointer to the last component or to the path itself.
115 */
116static char *get_last_path_component(char *path)
117{
118 char *ptr;
119
120 ptr = str_rchr(path, '/');
121 if (!ptr)
122 return path;
123 else
124 return ptr + 1;
125}
126
127/** Merge two paths together.
128 *
129 * e.g. (path1 = /data/dir, path2 = a/b) --> /data/dir/a/b
130 *
131 * @param path1 Path to which path2 will be appended.
132 * @param path1_size Size of the path1 buffer.
133 * @param path2 Path that will be appended to path1.
134 */
135static void merge_paths(char *path1, size_t path1_size, char *path2)
136{
137 const char *delim = "/";
138
139 str_rtrim(path1, '/');
140 str_append(path1, path1_size, delim);
141 str_append(path1, path1_size, path2);
142}
143
144static bool get_user_decision(bool bdefault, const char *message, ...)
145{
146 va_list args;
147
148 va_start(args, message);
149 vprintf(message, args);
150 va_end(args);
151
152 while (true) {
153 cons_event_t ev;
154 console_flush(con);
155 console_get_event(con, &ev);
156 if (ev.type != CEV_KEY || ev.ev.key.type != KEY_PRESS ||
157 (ev.ev.key.mods & (KM_CTRL | KM_ALT)) != 0) {
158 continue;
159 }
160
161 switch(ev.ev.key.key) {
162 case KC_Y:
163 printf("y\n");
164 return true;
165 case KC_N:
166 printf("n\n");
167 return false;
168 case KC_ENTER:
169 printf("%c\n", bdefault ? 'Y' : 'N');
170 return bdefault;
171 default:
172 break;
173 }
174 }
175}
176
177static int64_t do_copy(const char *src, const char *dest,
178 size_t blen, int vb, int recursive, int force, int interactive)
179{
180 int r = -1;
181 char dest_path[PATH_MAX];
182 char src_path[PATH_MAX];
183 DIR *dir = NULL;
184 struct dirent *dp;
185
186 dentry_type_t src_type = get_type(src);
187 dentry_type_t dest_type = get_type(dest);
188
189 const size_t src_len = str_size(src);
190
191 if (src_type == TYPE_FILE) {
192 char *src_fname;
193
194 /* Initialize the src_path with the src argument */
195 str_cpy(src_path, src_len + 1, src);
196 str_rtrim(src_path, '/');
197
198 /* Get the last component name from the src path */
199 src_fname = get_last_path_component(src_path);
200
201 /* Initialize dest_path with the dest argument */
202 str_cpy(dest_path, PATH_MAX, dest);
203
204 if (dest_type == TYPE_DIR) {
205 /* e.g. cp file_name /data */
206 /* e.g. cp file_name /data/ */
207
208 /* dest is a directory,
209 * append the src filename to it.
210 */
211 merge_paths(dest_path, PATH_MAX, src_fname);
212 dest_type = get_type(dest_path);
213 } else if (dest_type == TYPE_NONE) {
214 if (dest_path[str_size(dest_path) - 1] == '/') {
215 /* e.g. cp /textdemo /data/dirnotexists/ */
216
217 printf("The dest directory %s does not exists",
218 dest_path);
219 goto exit;
220 }
221 }
222
223 if (dest_type == TYPE_DIR) {
224 printf("Cannot overwrite existing directory %s\n",
225 dest_path);
226 goto exit;
227 } else if (dest_type == TYPE_FILE) {
228 /* e.g. cp file_name existing_file */
229
230 /* dest already exists,
231 * if force is set we will try to remove it.
232 * if interactive is set user input is required.
233 */
234 if (force && !interactive) {
235 if (vfs_unlink_path(dest_path) != EOK) {
236 printf("Unable to remove %s\n",
237 dest_path);
238 goto exit;
239 }
240 } else if (!force && interactive) {
241 bool overwrite = get_user_decision(false,
242 "File already exists: %s. Overwrite? [y/N]: ",
243 dest_path);
244 if (overwrite) {
245 printf("Overwriting file: %s\n", dest_path);
246 if (vfs_unlink_path(dest_path) != EOK) {
247 printf("Unable to remove %s\n", dest_path);
248 goto exit;
249 }
250 } else {
251 printf("Not overwriting file: %s\n", dest_path);
252 r = 0;
253 goto exit;
254 }
255 } else {
256 printf("File already exists: %s\n", dest_path);
257 goto exit;
258 }
259 }
260
261 /* call copy_file and exit */
262 r = (copy_file(src, dest_path, blen, vb) < 0);
263
264 } else if (src_type == TYPE_DIR) {
265 /* e.g. cp -r /x/srcdir /y/destdir/ */
266
267 if (!recursive) {
268 printf("Cannot copy the %s directory without the "
269 "-r option\n", src);
270 goto exit;
271 } else if (dest_type == TYPE_FILE) {
272 printf("Cannot overwrite a file with a directory\n");
273 goto exit;
274 }
275
276 char *src_dirname;
277
278 /* Initialize src_path with the content of src */
279 str_cpy(src_path, src_len + 1, src);
280 str_rtrim(src_path, '/');
281
282 src_dirname = get_last_path_component(src_path);
283
284 str_cpy(dest_path, PATH_MAX, dest);
285
286 switch (dest_type) {
287 case TYPE_DIR:
288 if (str_cmp(src_dirname, "..") &&
289 str_cmp(src_dirname, ".")) {
290 /* The last component of src_path is
291 * not '.' or '..'
292 */
293 merge_paths(dest_path, PATH_MAX, src_dirname);
294
295 if (vfs_link_path(dest_path, KIND_DIRECTORY,
296 NULL) != EOK) {
297 printf("Unable to create "
298 "dest directory %s\n", dest_path);
299 goto exit;
300 }
301 }
302 break;
303 default:
304 case TYPE_NONE:
305 /* dest does not exists, this means the user wants
306 * to specify the name of the destination directory
307 *
308 * e.g. cp -r /src /data/new_dir_src
309 */
310 if (vfs_link_path(dest_path, KIND_DIRECTORY,
311 NULL) != EOK) {
312 printf("Unable to create "
313 "dest directory %s\n", dest_path);
314 goto exit;
315 }
316 break;
317 }
318
319 dir = opendir(src);
320 if (!dir) {
321 /* Something strange is happening... */
322 printf("Unable to open src %s directory\n", src);
323 goto exit;
324 }
325
326 /* Copy every single directory entry of src into the
327 * destination directory.
328 */
329 while ((dp = readdir(dir))) {
330 struct stat src_s;
331 struct stat dest_s;
332
333 char src_dent[PATH_MAX];
334 char dest_dent[PATH_MAX];
335
336 str_cpy(src_dent, PATH_MAX, src);
337 merge_paths(src_dent, PATH_MAX, dp->d_name);
338
339 str_cpy(dest_dent, PATH_MAX, dest_path);
340 merge_paths(dest_dent, PATH_MAX, dp->d_name);
341
342 /* Check if we are copying a directory into itself */
343 vfs_stat_path(src_dent, &src_s);
344 vfs_stat_path(dest_path, &dest_s);
345
346 if (dest_s.index == src_s.index &&
347 dest_s.fs_handle == src_s.fs_handle) {
348 printf("Cannot copy a directory "
349 "into itself\n");
350 goto exit;
351 }
352
353 if (vb)
354 printf("copy %s %s\n", src_dent, dest_dent);
355
356 /* Recursively call do_copy() */
357 r = do_copy(src_dent, dest_dent, blen, vb, recursive,
358 force, interactive);
359 if (r)
360 goto exit;
361
362 }
363 } else
364 printf("Unable to open source file %s\n", src);
365
366exit:
367 if (dir)
368 closedir(dir);
369 return r;
370}
371
372static int64_t copy_file(const char *src, const char *dest,
373 size_t blen, int vb)
374{
375 int fd1, fd2, bytes;
376 off64_t total;
377 int64_t copied = 0;
378 char *buff = NULL;
379 aoff64_t posr = 0, posw = 0;
380 struct stat st;
381
382 if (vb)
383 printf("Copying %s to %s\n", src, dest);
384
385 fd1 = vfs_lookup_open(src, WALK_REGULAR, MODE_READ);
386 if (fd1 < 0) {
387 printf("Unable to open source file %s\n", src);
388 return -1;
389 }
390
391 fd2 = vfs_lookup_open(dest, WALK_REGULAR | WALK_MAY_CREATE, MODE_WRITE);
392 if (fd2 < 0) {
393 printf("Unable to open destination file %s\n", dest);
394 vfs_put(fd1);
395 return -1;
396 }
397
398 if (vfs_stat(fd1, &st) != EOK) {
399 printf("Unable to fstat %d\n", fd1);
400 vfs_put(fd1);
401 vfs_put(fd2);
402 return -1;
403 }
404
405 total = st.size;
406 if (vb)
407 printf("%" PRIu64 " bytes to copy\n", total);
408
409 if (NULL == (buff = (char *) malloc(blen))) {
410 printf("Unable to allocate enough memory to read %s\n",
411 src);
412 copied = -1;
413 goto out;
414 }
415
416 while ((bytes = vfs_read(fd1, &posr, buff, blen)) > 0) {
417 if ((bytes = vfs_write(fd2, &posw, buff, bytes)) < 0)
418 break;
419 copied += bytes;
420 }
421
422 if (bytes < 0) {
423 printf("\nError copying %s, (%d)\n", src, bytes);
424 copied = bytes;
425 }
426
427out:
428 vfs_put(fd1);
429 vfs_put(fd2);
430 if (buff)
431 free(buff);
432 return copied;
433}
434
435void help_cmd_cp(unsigned int level)
436{
437 static char helpfmt[] =
438 "Usage: %s [options] <source> <dest>\n"
439 "Options:\n"
440 " -h, --help A short option summary\n"
441 " -v, --version Print version information and exit\n"
442 " -V, --verbose Be annoyingly noisy about what's being done\n"
443 " -f, --force Do not complain when <dest> exists (overrides a previous -i)\n"
444 " -i, --interactive Ask what to do when <dest> exists (overrides a previous -f)\n"
445 " -r, --recursive Copy entire directories\n"
446 " -b, --buffer ## Set the read buffer size to ##\n";
447 if (level == HELP_SHORT) {
448 printf("`%s' copies files and directories\n", cmdname);
449 } else {
450 help_cmd_cp(HELP_SHORT);
451 printf(helpfmt, cmdname);
452 }
453
454 return;
455}
456
457int cmd_cp(char **argv)
458{
459 unsigned int argc, verbose = 0;
460 int buffer = 0, recursive = 0;
461 int force = 0, interactive = 0;
462 int c, opt_ind;
463 int64_t ret;
464
465 con = console_init(stdin, stdout);
466 argc = cli_count_args(argv);
467
468 for (c = 0, optreset = 1, optind = 0, opt_ind = 0; c != -1;) {
469 c = getopt_long(argc, argv, "hvVfirb:", long_options, &opt_ind);
470 switch (c) {
471 case 'h':
472 help_cmd_cp(1);
473 return CMD_SUCCESS;
474 case 'v':
475 printf("%s\n", CP_VERSION);
476 return CMD_SUCCESS;
477 case 'V':
478 verbose = 1;
479 break;
480 case 'f':
481 interactive = 0;
482 force = 1;
483 break;
484 case 'i':
485 force = 0;
486 interactive = 1;
487 break;
488 case 'r':
489 recursive = 1;
490 break;
491 case 'b':
492 if (-1 == (buffer = strtoint(optarg))) {
493 printf("%s: Invalid buffer specification, "
494 "(should be a number greater than zero)\n",
495 cmdname);
496 console_done(con);
497 return CMD_FAILURE;
498 }
499 if (verbose)
500 printf("Buffer = %d\n", buffer);
501 break;
502 }
503 }
504
505 if (buffer == 0)
506 buffer = CP_DEFAULT_BUFLEN;
507
508 argc -= optind;
509
510 if (argc != 2) {
511 printf("%s: invalid number of arguments. Try %s --help\n",
512 cmdname, cmdname);
513 console_done(con);
514 return CMD_FAILURE;
515 }
516
517 ret = do_copy(argv[optind], argv[optind + 1], buffer, verbose,
518 recursive, force, interactive);
519
520 console_done(con);
521
522 if (ret == 0)
523 return CMD_SUCCESS;
524 else
525 return CMD_FAILURE;
526}
527
Note: See TracBrowser for help on using the repository browser.