Annotation of embedaddon/pimd/libite/copyfile.c, revision 1.1.1.1

1.1       misho       1: /* Fastinit (finit) copyfile() implementation.
                      2:  *
                      3:  * Copyright (c) 2008 Claudio Matsuoka <http://helllabs.org/finit/>
                      4:  *
                      5:  * Permission is hereby granted, free of charge, to any person obtaining a copy
                      6:  * of this software and associated documentation files (the "Software"), to deal
                      7:  * in the Software without restriction, including without limitation the rights
                      8:  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
                      9:  * copies of the Software, and to permit persons to whom the Software is
                     10:  * furnished to do so, subject to the following conditions:
                     11:  *
                     12:  * The above copyright notice and this permission notice shall be included in
                     13:  * all copies or substantial portions of the Software.
                     14:  *
                     15:  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
                     16:  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
                     17:  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
                     18:  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
                     19:  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
                     20:  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
                     21:  * THE SOFTWARE.
                     22:  */
                     23: 
                     24: #include <errno.h>
                     25: #include <fcntl.h>
                     26: #include <stdio.h>
                     27: #include <stdlib.h>
                     28: #include <sys/stat.h>
                     29: 
                     30: #include "lite.h"
                     31: 
                     32: 
                     33: /* Tests if dst is a directory, if so, reallocates dst and appends src filename returning 1 */
                     34: static int adjust_target(char *src, char **dst)
                     35: {
                     36:        int isdir = 0;
                     37: 
                     38:        if (fisdir(*dst)) {
                     39:                int slash = 0;
                     40:                char *tmp, *ptr = strrchr(src, '/');
                     41: 
                     42:                if (!ptr)
                     43:                        ptr = src;
                     44:                else
                     45:                        ptr++;
                     46: 
                     47:                tmp = malloc(strlen(*dst) + strlen(ptr) + 2);
                     48:                if (!tmp) {
                     49:                        errno = EISDIR;
                     50:                        return 0;
                     51:                }
                     52: 
                     53:                isdir = 1;      /* Free dst before exit! */
                     54:                slash = fisslashdir(*dst);
                     55: 
                     56:                sprintf(tmp, "%s%s%s", *dst, slash ? "" : "/", ptr);
                     57:                *dst = tmp;
                     58:        }
                     59: 
                     60:        return isdir;
                     61: }
                     62: 
                     63: /* Actual copy loop, used by both copyfile() and fcopyfile()
                     64:  * breaks loop on error and EOF */
                     65: static ssize_t do_copy(int in, int out, size_t num, char *buffer, size_t len)
                     66: {
                     67:        ssize_t ret = 0, size = 0;
                     68: 
                     69:        do {
                     70:                size_t count = num > len ? len : num;
                     71: 
                     72:                ret = read(in, buffer, count);
                     73:                if (ret <= 0) {
                     74:                        if (ret == -1 && EINTR == errno)
                     75:                                continue;
                     76:                        break;
                     77:                }
                     78: 
                     79:                if (ret > 0)
                     80:                        size += write(out, buffer, ret);
                     81:                num  -= count;
                     82:        } while (num > 0);
                     83: 
                     84:        return size;
                     85: }
                     86: 
                     87: /**
                     88:  * copyfile - Copy a file to another.
                     89:  * @src: Full path name to source file.
                     90:  * @dst: Full path name to target file.
                     91:  * @len: Number of bytes to copy, zero (0) for entire file.
                     92:  * @sym: Should symlinks be recreated (1) or followed (0)
                     93:  *
                     94:  * This is a C implementation of the command line cp(1) utility.  It is one
                     95:  * of the classic missing links in the UNIX C library.  This version is from
                     96:  * the finit project, http://helllabs.org/finit/, which is a reimplementation
                     97:  * of fastinit for the Asus EeePC.
                     98:  *
                     99:  * Returns:
                    100:  * The number of bytes copied, zero may be error (check errno!), but it
                    101:  * may also indicate that @src was empty.  If @src is a directory @errno
                    102:  * will be set to %EISDIR since copyfile() is not recursive.
                    103:  */
                    104: ssize_t copyfile(char *src, char *dst, int len, int sym)
                    105: {
                    106:        char *buffer;
                    107:        int in, out, isdir = 0, saved_errno = 0;
                    108:        size_t num;
                    109:        ssize_t size = 0;
                    110:        struct stat st;
                    111: 
                    112:        errno = 0;
                    113: 
                    114:        buffer = malloc(BUFSIZ);
                    115:        if (!buffer)
                    116:                return 0;
                    117: 
                    118:        if (fisdir(src)) {
                    119:                saved_errno = EISDIR;   /* Error: source is a directory */
                    120:                goto exit;
                    121:        }
                    122: 
                    123:        /* Check if target is a directory, then append src filename. */
                    124:        isdir = adjust_target(src, &dst);
                    125: 
                    126:        /* Check if the source file is a symlink ... */
                    127:        if (stat(src, &st)) {
                    128:                size = -1;
                    129:                goto exit;
                    130:        }
                    131: 
                    132:        /* ... only try readlink() if symlink and @sym is set. */
                    133:        if (sym && (st.st_mode & S_IFMT) == S_IFLNK) {
                    134:                size = readlink(src, buffer, BUFSIZ);
                    135:                if (size > 0) {
                    136:                        if (size >= (ssize_t)BUFSIZ) {
                    137:                                saved_errno = ENOBUFS;
                    138:                                size = -1;
                    139:                        } else {
                    140:                                buffer[size] = 0;
                    141:                                size = !symlink(buffer, dst);
                    142:                        }
                    143:                }
                    144: 
                    145:                /* Don't fall back to copy, that is a potentially
                    146:                 * dangerous race condition, see CWE-367. */
                    147:                goto exit;
                    148:        }
                    149: 
                    150:        /* 0: copy entire file */
                    151:        if (len == 0)
                    152:                num = st.st_size;
                    153:        else
                    154:                num = (size_t)len;
                    155: 
                    156:        in = open(src, O_RDONLY);
                    157:        if (in < 0) {
                    158:                saved_errno = errno;
                    159:                goto exit;
                    160:        }
                    161: 
                    162:        out = open(dst, O_WRONLY | O_CREAT | O_TRUNC, fmode(src));
                    163:        if (out < 0) {
                    164:                close(in);
                    165:                saved_errno = errno;
                    166:                goto exit;
                    167:        }
                    168: 
                    169:        size = do_copy(in, out, num, buffer, BUFSIZ);
                    170:        if (!size && errno)
                    171:                saved_errno = errno;
                    172: 
                    173:        close(out);
                    174:        close(in);
                    175: 
                    176: exit:
                    177:        free(buffer);
                    178:        if (isdir)
                    179:                free(dst);
                    180:        errno = saved_errno;
                    181: 
                    182:        return size;
                    183: }
                    184: 
                    185: /**
                    186:  * movefile - Move a file to another location
                    187:  * @src: Source file.
                    188:  * @dst: Target file, or location.
                    189:  *
                    190:  * This is a C implementation of the command line mv(1) utility.
                    191:  * Usually the rename() API is sufficient, but not when moving across
                    192:  * file system boundaries.
                    193:  *
                    194:  * The @src argument must include the full path to the source file,
                    195:  * whereas the @dst argument may only be a directory, in which case the
                    196:  * same file name from @src is used.
                    197:  *
                    198:  * Returns:
                    199:  * POSIX OK(0), or non-zero with errno set.
                    200:  */
                    201: int movefile(char *src, char *dst)
                    202: {
                    203:        int isdir, result = 0;
                    204: 
                    205:        /* Check if target is a directory, then append the src base filename. */
                    206:        isdir = adjust_target(src, &dst);
                    207: 
                    208:        if (rename(src, dst)) {
                    209:                if (errno == EXDEV) {
                    210:                        errno = 0;
                    211:                        copyfile(src, dst, 0, 1);
                    212:                        if (errno)
                    213:                                result = 1;
                    214:                        else
                    215:                                result = remove(src);
                    216:                } else {
                    217:                        result = 1;
                    218:                }
                    219:        }
                    220: 
                    221:        if (isdir)
                    222:                free(dst);
                    223: 
                    224:        return result;
                    225: }
                    226: 
                    227: /**
                    228:  * fcopyfile - Copy between FILE *fp.
                    229:  * @src: Source FILE.
                    230:  * @dst: Destination FILE.
                    231:  *
                    232:  * Function takes signals into account and will restart the syscalls as
                    233:  * long as error is %EINTR.
                    234:  *
                    235:  * Returns:
                    236:  * POSIX OK(0), or non-zero with errno set on error.
                    237:  */
                    238: int fcopyfile(FILE *src, FILE *dst)
                    239: {
                    240:        int ret;
                    241:        char *buffer;
                    242: 
                    243:        if (!src || !dst) {
                    244:                errno = EINVAL;
                    245:                return -1;
                    246:        }
                    247: 
                    248:        buffer = malloc(BUFSIZ);
                    249:        if (!buffer)
                    250:                return -1;
                    251: 
                    252:        ret = do_copy(fileno(src), fileno(dst), BUFSIZ, buffer, BUFSIZ);
                    253:        if (ret > 0)
                    254:                ret = 0;
                    255:        else if (errno)
                    256:                ret = -1;
                    257: 
                    258:        free(buffer);
                    259: 
                    260:        return ret;
                    261: }
                    262: 
                    263: #ifdef UNITTEST
                    264: #include <err.h>
                    265: 
                    266: int main(void)
                    267: {
                    268:        int i = 0;
                    269:        char *files[] = {
                    270:                "/etc/passwd", "/tmp/tok", "/tmp/tok",
                    271:                "/etc/passwd", "/tmp/", "/tmp/passwd",
                    272:                "/etc/passwd", "/tmp", "/tmp/passwd",
                    273:                NULL
                    274:        };
                    275:        FILE *src, *dst;
                    276: 
                    277:        printf("=>Start testing fcopyfile()\n");
                    278:        while (files[i]) {
                    279:                printf("fcopyfile(%s, %s)\t", files[i], files[i + 1]);
                    280:                src = fopen(files[i], "r");
                    281:                dst = fopen(files[i + 1], "w");
                    282:                if (fcopyfile(src, dst)) {
                    283:                        if (!fisdir(files[i + 1]))
                    284:                            err(1, "Failed fcopyfile(%s, %s)", files[i], files[i + 1]);
                    285:                }
                    286: 
                    287:                if (src)
                    288:                        fclose(src);
                    289:                if (dst)
                    290:                        fclose(dst);
                    291: 
                    292:                if (fexist(files[i + 2]))
                    293:                        printf("OK => %s", files[i + 2]);
                    294:                printf("\n");
                    295: 
                    296:                erase(files[i + 2]);
                    297:                i += 3;
                    298:        }
                    299: 
                    300:        printf("\n=>Start testing copyfile()\n");
                    301:        i = 0;
                    302:        while (files[i]) {
                    303:                printf("copyfile(%s, %s)\t", files[i], files[i + 1]);
                    304:                if (!copyfile(files[i], files[i + 1], 0, 0))
                    305:                        err(1, "Failed copyfile(%s, %s)", files[i], files[i + 1]);
                    306: 
                    307:                if (fexist(files[i + 2]))
                    308:                        printf("OK => %s", files[i + 2]);
                    309:                printf("\n");
                    310: 
                    311:                erase(files[i + 2]);
                    312:                i += 3;
                    313:        }
                    314: 
                    315:        return 0;
                    316: }
                    317: #endif /* UNITTEST */
                    318: 
                    319: /**
                    320:  * Local Variables:
                    321:  *  compile-command: "make V=1 -f copyfile.mk"
                    322:  *  version-control: t
                    323:  *  indent-tabs-mode: t
                    324:  *  c-file-style: "linux"
                    325:  * End:
                    326:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>