Annotation of embedaddon/pimd/libite/copyfile.c, revision 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>