Пример #1
0
static int
file_open(struct archive *a, void *client_data)
{
	struct stat st;
	struct read_file_data *mine = (struct read_file_data *)client_data;
	void *buffer;
	const char *filename = NULL;
	const wchar_t *wfilename = NULL;
	int fd;
	int is_disk_like = 0;
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
	off_t mediasize = 0; /* FreeBSD-specific, so off_t okay here. */
#elif defined(__NetBSD__) || defined(__OpenBSD__)
	struct disklabel dl;
#elif defined(__DragonFly__)
	struct partinfo pi;
#endif

	archive_clear_error(a);
	if (mine->filename_type == FNT_STDIN) {
		/* We used to delegate stdin support by
		 * directly calling archive_read_open_fd(a,0,block_size)
		 * here, but that doesn't (and shouldn't) handle the
		 * end-of-file flush when reading stdout from a pipe.
		 * Basically, read_open_fd() is intended for folks who
		 * are willing to handle such details themselves.  This
		 * API is intended to be a little smarter for folks who
		 * want easy handling of the common case.
		 */
		fd = 0;
#if defined(__CYGWIN__) || defined(_WIN32)
		setmode(0, O_BINARY);
#endif
		filename = "";
	} else if (mine->filename_type == FNT_MBS) {
		filename = mine->filename.m;
		fd = open(filename, O_RDONLY | O_BINARY | O_CLOEXEC);
		__archive_ensure_cloexec_flag(fd);
		if (fd < 0) {
			archive_set_error(a, errno,
			    "Failed to open '%s'", filename);
			return (ARCHIVE_FATAL);
		}
	} else {
#if defined(_WIN32) && !defined(__CYGWIN__)
		wfilename = mine->filename.w;
		fd = _wopen(wfilename, O_RDONLY | O_BINARY);
		if (fd < 0 && errno == ENOENT) {
			wchar_t *fullpath;
			fullpath = __la_win_permissive_name_w(wfilename);
			if (fullpath != NULL) {
				fd = _wopen(fullpath, O_RDONLY | O_BINARY);
				free(fullpath);
			}
		}
		if (fd < 0) {
			archive_set_error(a, errno,
			    "Failed to open '%S'", wfilename);
			return (ARCHIVE_FATAL);
		}
#else
		archive_set_error(a, ARCHIVE_ERRNO_MISC,
		    "Unexpedted operation in archive_read_open_filename");
		return (ARCHIVE_FATAL);
#endif
	}
	if (fstat(fd, &st) != 0) {
		if (mine->filename_type == FNT_WCS)
			archive_set_error(a, errno, "Can't stat '%S'",
			    wfilename);
		else
			archive_set_error(a, errno, "Can't stat '%s'",
			    filename);
		return (ARCHIVE_FATAL);
	}

	/*
	 * Determine whether the input looks like a disk device or a
	 * tape device.  The results are used below to select an I/O
	 * strategy:
	 *  = "disk-like" devices support arbitrary lseek() and will
	 *    support I/O requests of any size.  So we get easy skipping
	 *    and can cheat on block sizes to get better performance.
	 *  = "tape-like" devices require strict blocking and use
	 *    specialized ioctls for seeking.
	 *  = "socket-like" devices cannot seek at all but can improve
	 *    performance by using nonblocking I/O to read "whatever is
	 *    available right now".
	 *
	 * Right now, we only specially recognize disk-like devices,
	 * but it should be straightforward to add probes and strategy
	 * here for tape-like and socket-like devices.
	 */
	if (S_ISREG(st.st_mode)) {
		/* Safety:  Tell the extractor not to overwrite the input. */
		archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
		/* Regular files act like disks. */
		is_disk_like = 1;
	}
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
	/* FreeBSD: if it supports DIOCGMEDIASIZE ioctl, it's disk-like. */
	else if (S_ISCHR(st.st_mode) &&
	    ioctl(fd, DIOCGMEDIASIZE, &mediasize) == 0 &&
	    mediasize > 0) {
		is_disk_like = 1;
	}
#elif defined(__NetBSD__) || defined(__OpenBSD__)
	/* Net/OpenBSD: if it supports DIOCGDINFO ioctl, it's disk-like. */
	else if ((S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) &&
	    ioctl(fd, DIOCGDINFO, &dl) == 0 &&
	    dl.d_partitions[DISKPART(st.st_rdev)].p_size > 0) {
		is_disk_like = 1;
	}
#elif defined(__DragonFly__)
	/* DragonFly BSD:  if it supports DIOCGPART ioctl, it's disk-like. */
	else if (S_ISCHR(st.st_mode) &&
	    ioctl(fd, DIOCGPART, &pi) == 0 &&
	    pi.media_size > 0) {
		is_disk_like = 1;
	}
#elif defined(__linux__)
	/* Linux:  All block devices are disk-like. */
	else if (S_ISBLK(st.st_mode) &&
	    lseek(fd, 0, SEEK_CUR) == 0 &&
	    lseek(fd, 0, SEEK_SET) == 0 &&
	    lseek(fd, 0, SEEK_END) > 0 &&
	    lseek(fd, 0, SEEK_SET) == 0) {
		is_disk_like = 1;
	}
#endif
	/* TODO: Add an "is_tape_like" variable and appropriate tests. */

	/* Disk-like devices prefer power-of-two block sizes.  */
	/* Use provided block_size as a guide so users have some control. */
	if (is_disk_like) {
		size_t new_block_size = 64 * 1024;
		while (new_block_size < mine->block_size
		    && new_block_size < 64 * 1024 * 1024)
			new_block_size *= 2;
		mine->block_size = new_block_size;
	}
	buffer = malloc(mine->block_size);
	if (mine == NULL || buffer == NULL) {
		archive_set_error(a, ENOMEM, "No memory");
		free(mine);
		free(buffer);
		return (ARCHIVE_FATAL);
	}
	mine->buffer = buffer;
	mine->fd = fd;
	/* Remember mode so close can decide whether to flush. */
	mine->st_mode = st.st_mode;

	/* Disk-like inputs can use lseek(). */
	if (is_disk_like)
		mine->use_lseek = 1;

	return (ARCHIVE_OK);
}
Пример #2
0
static int
file_open(struct archive *a, void *client_data)
{
	int flags;
	struct write_file_data *mine;
	struct stat st;
#if defined(_WIN32) && !defined(__CYGWIN__)
	wchar_t *fullpath;
#endif
	const wchar_t *wcs;
	const char *mbs;

	mine = (struct write_file_data *)client_data;
	flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_CLOEXEC;

	/*
	 * Open the file.
	 */
	mbs = NULL; wcs = NULL;
#if defined(_WIN32) && !defined(__CYGWIN__)
	if (archive_mstring_get_wcs(a, &mine->filename, &wcs) != 0) {
		if (errno == ENOMEM)
			archive_set_error(a, errno, "No memory");
		else {
			archive_mstring_get_mbs(a, &mine->filename, &mbs);
			archive_set_error(a, errno,
			    "Can't convert '%s' to WCS", mbs);
		}
		return (ARCHIVE_FATAL);
	}
	fullpath = __la_win_permissive_name_w(wcs);
	if (fullpath != NULL) {
		mine->fd = _wopen(fullpath, flags, 0666);
		free(fullpath);
	} else
		mine->fd = _wopen(wcs, flags, 0666);
#else
	if (archive_mstring_get_mbs(a, &mine->filename, &mbs) != 0) {
		if (errno == ENOMEM)
			archive_set_error(a, errno, "No memory");
		else {
			archive_mstring_get_wcs(a, &mine->filename, &wcs);
			archive_set_error(a, errno,
			    "Can't convert '%S' to MBS", wcs);
		}
		return (ARCHIVE_FATAL);
	}
	mine->fd = open(mbs, flags, 0666);
	__archive_ensure_cloexec_flag(mine->fd);
#endif
	if (mine->fd < 0) {
		if (mbs != NULL)
			archive_set_error(a, errno, "Failed to open '%s'", mbs);
		else
			archive_set_error(a, errno, "Failed to open '%S'", wcs);
		return (ARCHIVE_FATAL);
	}

	if (fstat(mine->fd, &st) != 0) {
		if (mbs != NULL)
			archive_set_error(a, errno, "Couldn't stat '%s'", mbs);
		else
			archive_set_error(a, errno, "Couldn't stat '%S'", wcs);
		return (ARCHIVE_FATAL);
	}

	/*
	 * Set up default last block handling.
	 */
	if (archive_write_get_bytes_in_last_block(a) < 0) {
		if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) ||
		    S_ISFIFO(st.st_mode))
			/* Pad last block when writing to device or FIFO. */
			archive_write_set_bytes_in_last_block(a, 0);
		else
			/* Don't pad last block otherwise. */
			archive_write_set_bytes_in_last_block(a, 1);
	}

	/*
	 * If the output file is a regular file, don't add it to
	 * itself.  If it's a device file, it's okay to add the device
	 * entry to the output archive.
	 */
	if (S_ISREG(st.st_mode))
		archive_write_set_skip_file(a, st.st_dev, st.st_ino);

	return (ARCHIVE_OK);
}
Пример #3
0
/*
 * Do not use Windows tmpfile() function.
 * It will make a temporary file under the root directory
 * and it'll cause permission error if a user who is
 * non-Administrator creates temporary files.
 * Also Windows version of mktemp family including _mktemp_s
 * are not secure.
 */
int
__archive_mktemp(const char *tmpdir)
{
	static const wchar_t *prefix = L"libarchive_";
	static const wchar_t *suffix = L"XXXXXXXXXX";
	static const wchar_t num[] = {
		L'0', L'1', L'2', L'3', L'4', L'5', L'6', L'7',
		L'8', L'9', L'A', L'B', L'C', L'D', L'E', L'F',
		L'G', L'H', L'I', L'J', L'K', L'L', L'M', L'N',
		L'O', L'P', L'Q', L'R', L'S', L'T', L'U', L'V',
		L'W', L'X', L'Y', L'Z', L'a', L'b', L'c', L'd',
		L'e', L'f', L'g', L'h', L'i', L'j', L'k', L'l',
		L'm', L'n', L'o', L'p', L'q', L'r', L's', L't',
		L'u', L'v', L'w', L'x', L'y', L'z'
	};
	HCRYPTPROV hProv;
	struct archive_wstring temp_name;
	wchar_t *ws;
	DWORD attr;
	wchar_t *xp, *ep;
	int fd;

	hProv = (HCRYPTPROV)NULL;
	fd = -1;
	ws = NULL;
	archive_string_init(&temp_name);

	/* Get a temporary directory. */
	if (tmpdir == NULL) {
		size_t l;
		wchar_t *tmp;

		l = GetTempPathW(0, NULL);
		if (l == 0) {
			la_dosmaperr(GetLastError());
			goto exit_tmpfile;
		}
		tmp = malloc(l*sizeof(wchar_t));
		if (tmp == NULL) {
			errno = ENOMEM;
			goto exit_tmpfile;
		}
		GetTempPathW((DWORD)l, tmp);
		archive_wstrcpy(&temp_name, tmp);
		free(tmp);
	} else {
		if (archive_wstring_append_from_mbs(&temp_name, tmpdir,
		    strlen(tmpdir)) < 0)
			goto exit_tmpfile;
		if (temp_name.s[temp_name.length-1] != L'/')
			archive_wstrappend_wchar(&temp_name, L'/');
	}

	/* Check if temp_name is a directory. */
	attr = GetFileAttributesW(temp_name.s);
	if (attr == (DWORD)-1) {
		if (GetLastError() != ERROR_FILE_NOT_FOUND) {
			la_dosmaperr(GetLastError());
			goto exit_tmpfile;
		}
		ws = __la_win_permissive_name_w(temp_name.s);
		if (ws == NULL) {
			errno = EINVAL;
			goto exit_tmpfile;
		}
		attr = GetFileAttributesW(ws);
		if (attr == (DWORD)-1) {
			la_dosmaperr(GetLastError());
			goto exit_tmpfile;
		}
	}
	if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
		errno = ENOTDIR;
		goto exit_tmpfile;
	}

	/*
	 * Create a temporary file.
	 */
	archive_wstrcat(&temp_name, prefix);
	archive_wstrcat(&temp_name, suffix);
	ep = temp_name.s + archive_strlen(&temp_name);
	xp = ep - wcslen(suffix);

	if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
		CRYPT_VERIFYCONTEXT)) {
		la_dosmaperr(GetLastError());
		goto exit_tmpfile;
	}

	for (;;) {
		wchar_t *p;
		HANDLE h;

		/* Generate a random file name through CryptGenRandom(). */
		p = xp;
		if (!CryptGenRandom(hProv, (DWORD)(ep - p)*sizeof(wchar_t),
		    (BYTE*)p)) {
			la_dosmaperr(GetLastError());
			goto exit_tmpfile;
		}
		for (; p < ep; p++)
			*p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))];

		free(ws);
		ws = __la_win_permissive_name_w(temp_name.s);
		if (ws == NULL) {
			errno = EINVAL;
			goto exit_tmpfile;
		}
		/* Specifies FILE_FLAG_DELETE_ON_CLOSE flag is to
		 * delete this temporary file immediately when this
		 * file closed. */
		h = CreateFileW(ws,
		    GENERIC_READ | GENERIC_WRITE | DELETE,
		    0,/* Not share */
		    NULL,
		    CREATE_NEW,/* Create a new file only */
		    FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
		    NULL);
		if (h == INVALID_HANDLE_VALUE) {
			/* The same file already exists. retry with
			 * a new filename. */
			if (GetLastError() == ERROR_FILE_EXISTS)
				continue;
			/* Otherwise, fail creation temporary file. */
			la_dosmaperr(GetLastError());
			goto exit_tmpfile;
		}
		fd = _open_osfhandle((intptr_t)h, _O_BINARY | _O_RDWR);
		if (fd == -1) {
			CloseHandle(h);
			goto exit_tmpfile;
		} else
			break;/* success! */
	}
exit_tmpfile:
	if (hProv != (HCRYPTPROV)NULL)
		CryptReleaseContext(hProv, 0);
	free(ws);
	archive_wstring_free(&temp_name);
	return (fd);
}