Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/app/bdsh/cmds/modules/cp/cp.c

    r1737bfb r2815505  
    1 /*
    2  * Copyright (c) 2008 Tim Post
     1/* Copyright (c) 2008, Tim Post <tinkertim@gmail.com>
    32 * All rights reserved.
    43 *
    54 * 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.
     5 * modification, are permitted provided that the following conditions are met:
     6 *
     7 * Redistributions of source code must retain the above copyright notice, this
     8 * list of conditions and the following disclaimer.
     9 *
     10 * Redistributions in binary form must reproduce the above copyright notice,
     11 * this list of conditions and the following disclaimer in the documentation
     12 * and/or other materials provided with the distribution.
     13 *
     14 * Neither the name of the original program's authors nor the names of its
     15 * contributors may be used to endorse or promote products derived from this
     16 * software without specific prior written permission.
     17 *
     18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
     22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     28 * POSSIBILITY OF SUCH DAMAGE.
    2729 */
    2830
     
    3335#include <str.h>
    3436#include <fcntl.h>
    35 #include <sys/stat.h>
    36 #include <dirent.h>
    3737#include "config.h"
    3838#include "util.h"
     
    5757};
    5858
    59 typedef enum {
    60         TYPE_NONE,
    61         TYPE_FILE,
    62         TYPE_DIR
    63 } dentry_type_t;
    64 
    65 static int64_t copy_file(const char *src, const char *dest,
    66     size_t blen, int vb);
    67 
    68 /** Get the type of a directory entry.
    69  *
    70  * @param path  Path of the directory entry.
    71  *
    72  * @return TYPE_DIR if the dentry is a directory.
    73  * @return TYPE_FILE if the dentry is a file.
    74  * @return TYPE_NONE if the dentry does not exists.
    75  */
    76 static dentry_type_t get_type(const char *path)
    77 {
    78         struct stat s;
    79 
    80         int r = stat(path, &s);
    81 
    82         if (r)
    83                 return TYPE_NONE;
    84         else if (s.is_directory)
    85                 return TYPE_DIR;
    86         else if (s.is_file)
    87                 return TYPE_FILE;
    88 
    89         return TYPE_NONE;
    90 }
    91 
    9259static int strtoint(const char *s1)
    9360{
     
    10269        return (int) t1;
    10370}
    104 
    105 /** Get the last component of a path.
    106  *
    107  * e.g. /data/a  ---> a
    108  *
    109  * @param path  Pointer to the path.
    110  *
    111  * @return      Pointer to the last component or to the path itself.
    112  */
    113 static char *get_last_path_component(char *path)
    114 {
    115         char *ptr;
    116 
    117         ptr = str_rchr(path, '/');
    118         if (!ptr)
    119                 return path;
    120         else
    121                 return ptr + 1;
    122 }
    123 
    124 /** Merge two paths together.
    125  *
    126  * e.g. (path1 = /data/dir, path2 = a/b) --> /data/dir/a/b
    127  *
    128  * @param path1         Path to which path2 will be appended.
    129  * @param path1_size    Size of the path1 buffer.
    130  * @param path2         Path that will be appended to path1.
    131  */
    132 static void merge_paths(char *path1, size_t path1_size, char *path2)
    133 {
    134         const char *delim = "/";
    135 
    136         str_rtrim(path1, '/');
    137         str_append(path1, path1_size, delim);
    138         str_append(path1, path1_size, path2);
    139 }
    140 
    141 static int64_t do_copy(const char *src, const char *dest,
    142     size_t blen, int vb, int recursive, int force)
    143 {
    144         int r = -1;
    145         char dest_path[PATH_MAX];
    146         char src_path[PATH_MAX];
    147         DIR *dir = NULL;
    148         struct dirent *dp;
    149 
    150         dentry_type_t src_type = get_type(src);
    151         dentry_type_t dest_type = get_type(dest);
    152 
    153         const size_t src_len = str_size(src);
    154 
    155         if (src_type == TYPE_FILE) {
    156                 char *src_fname;
    157 
    158                 /* Initialize the src_path with the src argument */
    159                 str_cpy(src_path, src_len + 1, src);
    160                 str_rtrim(src_path, '/');
    161                
    162                 /* Get the last component name from the src path */
    163                 src_fname = get_last_path_component(src_path);
    164                
    165                 /* Initialize dest_path with the dest argument */
    166                 str_cpy(dest_path, PATH_MAX, dest);
    167 
    168                 if (dest_type == TYPE_DIR) {
    169                         /* e.g. cp file_name /data */
    170                         /* e.g. cp file_name /data/ */
    171                        
    172                         /* dest is a directory,
    173                          * append the src filename to it.
    174                          */
    175                         merge_paths(dest_path, PATH_MAX, src_fname);
    176                         dest_type = get_type(dest_path);
    177                 } else if (dest_type == TYPE_NONE) {
    178                         if (dest_path[str_size(dest_path) - 1] == '/') {
    179                                 /* e.g. cp /textdemo /data/dirnotexists/ */
    180 
    181                                 printf("The dest directory %s does not exists",
    182                                     dest_path);
    183                                 goto exit;
    184                         }
    185                 }
    186 
    187                 if (dest_type == TYPE_DIR) {
    188                         printf("Cannot overwrite existing directory %s\n",
    189                             dest_path);
    190                         goto exit;
    191                 } else if (dest_type == TYPE_FILE) {
    192                         /* e.g. cp file_name existing_file */
    193 
    194                         /* dest already exists, if force is set we will
    195                          * try to remove it.
    196                          */
    197                         if (force) {
    198                                 if (unlink(dest_path)) {
    199                                         printf("Unable to remove %s\n",
    200                                             dest_path);
    201                                         goto exit;
    202                                 }
    203                         } else {
    204                                 printf("file already exists: %s\n", dest_path);
    205                                 goto exit;
    206                         }
    207                 }
    208 
    209                 /* call copy_file and exit */
    210                 r = (copy_file(src, dest_path, blen, vb) < 0);
    211 
    212         } else if (src_type == TYPE_DIR) {
    213                 /* e.g. cp -r /x/srcdir /y/destdir/ */
    214 
    215                 if (!recursive) {
    216                         printf("Cannot copy the %s directory without the "
    217                             "-r option\n", src);
    218                         goto exit;
    219                 } else if (dest_type == TYPE_FILE) {
    220                         printf("Cannot overwrite a file with a directory\n");
    221                         goto exit;
    222                 }
    223 
    224                 char *src_dirname;
    225 
    226                 /* Initialize src_path with the content of src */
    227                 str_cpy(src_path, src_len + 1, src);
    228                 str_rtrim(src_path, '/');
    229 
    230                 src_dirname = get_last_path_component(src_path);
    231 
    232                 str_cpy(dest_path, PATH_MAX, dest);
    233 
    234                 switch (dest_type) {
    235                 case TYPE_DIR:
    236                         if (str_cmp(src_dirname, "..") &&
    237                             str_cmp(src_dirname, ".")) {
    238                                 /* The last component of src_path is
    239                                  * not '.' or '..'
    240                                  */
    241                                 merge_paths(dest_path, PATH_MAX, src_dirname);
    242 
    243                                 if (mkdir(dest_path, 0) == -1) {
    244                                         printf("Unable to create "
    245                                             "dest directory %s\n", dest_path);
    246                                         goto exit;
    247                                 }
    248                         }
    249                         break;
    250                 default:
    251                 case TYPE_NONE:
    252                         /* dest does not exists, this means the user wants
    253                          * to specify the name of the destination directory
    254                          *
    255                          * e.g. cp -r /src /data/new_dir_src
    256                          */
    257                         if (mkdir(dest_path, 0)) {
    258                                 printf("Unable to create "
    259                                     "dest directory %s\n", dest_path);
    260                                 goto exit;
    261                         }
    262                         break;
    263                 }
    264 
    265                 dir = opendir(src);
    266                 if (!dir) {
    267                         /* Something strange is happening... */
    268                         printf("Unable to open src %s directory\n", src);
    269                         goto exit;
    270                 }
    271 
    272                 /* Copy every single directory entry of src into the
    273                  * destination directory.
    274                  */
    275                 while ((dp = readdir(dir))) {
    276                         struct stat src_s;
    277                         struct stat dest_s;
    278 
    279                         char src_dent[PATH_MAX];
    280                         char dest_dent[PATH_MAX];
    281 
    282                         str_cpy(src_dent, PATH_MAX, src);
    283                         merge_paths(src_dent, PATH_MAX, dp->d_name);
    284 
    285                         str_cpy(dest_dent, PATH_MAX, dest_path);
    286                         merge_paths(dest_dent, PATH_MAX, dp->d_name);
    287 
    288                         /* Check if we are copying a directory into itself */
    289                         stat(src_dent, &src_s);
    290                         stat(dest_path, &dest_s);
    291 
    292                         if (dest_s.index == src_s.index &&
    293                             dest_s.fs_handle == src_s.fs_handle) {
    294                                 printf("Cannot copy a directory "
    295                                     "into itself\n");
    296                                 goto exit;
    297                         }
    298 
    299                         if (vb)
    300                                 printf("copy %s %s\n", src_dent, dest_dent);
    301 
    302                         /* Recursively call do_copy() */
    303                         r = do_copy(src_dent, dest_dent, blen, vb, recursive,
    304                             force);
    305                         if (r)
    306                                 goto exit;
    307 
    308                 }
    309         } else
    310                 printf("Unable to open source file %s\n", src);
    311 
    312 exit:
    313         if (dir)
    314                 closedir(dir);
    315         return r;
    316 }
    317 
    31871
    31972static int64_t copy_file(const char *src, const char *dest,
    32073        size_t blen, int vb)
    32174{
    322         int fd1, fd2, bytes;
    323         off64_t total;
     75        int fd1, fd2, bytes = 0;
     76        off64_t total = 0;
    32477        int64_t copied = 0;
    32578        char *buff = NULL;
     
    353106        }
    354107
    355         while ((bytes = read_all(fd1, buff, blen)) > 0) {
    356                 if ((bytes = write_all(fd2, buff, bytes)) < 0)
     108        for (;;) {
     109                ssize_t res;
     110                size_t written = 0;
     111
     112                bytes = read(fd1, buff, blen);
     113                if (bytes <= 0)
    357114                        break;
    358115                copied += bytes;
     116                res = bytes;
     117                do {
     118                        /*
     119                         * Theoretically, it may not be enough to call write()
     120                         * only once. Also the previous read() may have
     121                         * returned less data than requested.
     122                         */
     123                        bytes = write(fd2, buff + written, res);
     124                        if (bytes < 0)
     125                                goto err;
     126                        written += bytes;
     127                        res -= bytes;
     128                } while (res > 0);
     129
     130                /* TODO: re-insert assert() once this is stand alone,
     131                 * removed as abort() exits the entire shell
     132                 */
     133                if (res != 0) {
     134                        printf("\n%zd more bytes than actually exist were copied\n", res);
     135                        goto err;
     136                }
    359137        }
    360138
    361139        if (bytes < 0) {
     140err:
    362141                printf("\nError copying %s, (%d)\n", src, bytes);
    363142                copied = bytes;
     
    376155        static char helpfmt[] =
    377156            "Usage:  %s [options] <source> <dest>\n"
    378             "Options:\n"
     157            "Options: (* indicates not yet implemented)\n"
    379158            "  -h, --help       A short option summary\n"
    380159            "  -v, --version    Print version information and exit\n"
    381             "  -V, --verbose    Be annoyingly noisy about what's being done\n"
    382             "  -f, --force      Do not complain when <dest> exists\n"
    383             "  -r, --recursive  Copy entire directories\n"
    384             "  -b, --buffer ## Set the read buffer size to ##\n";
     160            "* -V, --verbose    Be annoyingly noisy about what's being done\n"
     161            "* -f, --force      Do not complain when <dest> exists\n"
     162            "* -r, --recursive  Copy entire directories\n"
     163            "  -b, --buffer ## Set the read buffer size to ##\n"
     164            "Currently, %s is under development, some options may not work.\n";
    385165        if (level == HELP_SHORT) {
    386166                printf("`%s' copies files and directories\n", cmdname);
    387167        } else {
    388168                help_cmd_cp(HELP_SHORT);
    389                 printf(helpfmt, cmdname);
     169                printf(helpfmt, cmdname, cmdname);
    390170        }
    391171
     
    396176{
    397177        unsigned int argc, verbose = 0;
    398         int buffer = 0, recursive = 0;
    399         int force = 0;
     178        int buffer = 0;
    400179        int c, opt_ind;
    401180        int64_t ret;
     
    416195                        break;
    417196                case 'f':
    418                         force = 1;
    419197                        break;
    420198                case 'r':
    421                         recursive = 1;
    422199                        break;
    423200                case 'b':
     
    445222        }
    446223
    447         ret = do_copy(argv[optind], argv[optind + 1], buffer, verbose,
    448             recursive, force);
    449 
    450         if (ret == 0)
     224        ret = copy_file(argv[optind], argv[optind + 1], buffer, verbose);
     225
     226        if (verbose)
     227                printf("%" PRId64 " bytes copied\n", ret);
     228
     229        if (ret >= 0)
    451230                return CMD_SUCCESS;
    452231        else
Note: See TracChangeset for help on using the changeset viewer.