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>