File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / pimd / libite / copyfile.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 14 09:12:58 2017 UTC (7 years, 4 months ago) by misho
Branches: pimd, MAIN
CVS tags: v2_3_2, HEAD
libite

    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>