Ignore:
File:
1 edited

Legend:

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

    r2815505 r1737bfb  
    1 /* Copyright (c) 2008, Tim Post <tinkertim@gmail.com>
     1/*
     2 * Copyright (c) 2008 Tim Post
    23 * All rights reserved.
    34 *
    45 * Redistribution and use in source and binary forms, with or without
    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.
     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.
    2927 */
    3028
     
    3533#include <str.h>
    3634#include <fcntl.h>
     35#include <sys/stat.h>
     36#include <dirent.h>
    3737#include "config.h"
    3838#include "util.h"
     
    5757};
    5858
     59typedef enum {
     60        TYPE_NONE,
     61        TYPE_FILE,
     62        TYPE_DIR
     63} dentry_type_t;
     64
     65static 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 */
     76static 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
    5992static int strtoint(const char *s1)
    6093{
     
    70103}
    71104
     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 */
     113static 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 */
     132static 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
     141static 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
     312exit:
     313        if (dir)
     314                closedir(dir);
     315        return r;
     316}
     317
     318
    72319static int64_t copy_file(const char *src, const char *dest,
    73320        size_t blen, int vb)
    74321{
    75         int fd1, fd2, bytes = 0;
    76         off64_t total = 0;
     322        int fd1, fd2, bytes;
     323        off64_t total;
    77324        int64_t copied = 0;
    78325        char *buff = NULL;
     
    106353        }
    107354
    108         for (;;) {
    109                 ssize_t res;
    110                 size_t written = 0;
    111 
    112                 bytes = read(fd1, buff, blen);
    113                 if (bytes <= 0)
     355        while ((bytes = read_all(fd1, buff, blen)) > 0) {
     356                if ((bytes = write_all(fd2, buff, bytes)) < 0)
    114357                        break;
    115358                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                 }
    137359        }
    138360
    139361        if (bytes < 0) {
    140 err:
    141362                printf("\nError copying %s, (%d)\n", src, bytes);
    142363                copied = bytes;
     
    155376        static char helpfmt[] =
    156377            "Usage:  %s [options] <source> <dest>\n"
    157             "Options: (* indicates not yet implemented)\n"
     378            "Options:\n"
    158379            "  -h, --help       A short option summary\n"
    159380            "  -v, --version    Print version information and exit\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";
     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";
    165385        if (level == HELP_SHORT) {
    166386                printf("`%s' copies files and directories\n", cmdname);
    167387        } else {
    168388                help_cmd_cp(HELP_SHORT);
    169                 printf(helpfmt, cmdname, cmdname);
     389                printf(helpfmt, cmdname);
    170390        }
    171391
     
    176396{
    177397        unsigned int argc, verbose = 0;
    178         int buffer = 0;
     398        int buffer = 0, recursive = 0;
     399        int force = 0;
    179400        int c, opt_ind;
    180401        int64_t ret;
     
    195416                        break;
    196417                case 'f':
     418                        force = 1;
    197419                        break;
    198420                case 'r':
     421                        recursive = 1;
    199422                        break;
    200423                case 'b':
     
    222445        }
    223446
    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)
     447        ret = do_copy(argv[optind], argv[optind + 1], buffer, verbose,
     448            recursive, force);
     449
     450        if (ret == 0)
    230451                return CMD_SUCCESS;
    231452        else
Note: See TracChangeset for help on using the changeset viewer.