source: mainline/uspace/app/bdsh/cmds/modules/cp/cp.c@ 79ea5af

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

Rename unlink() to vfs_unlink_path() and _vfs_unlink() to vfs_unlink()

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