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); }
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); }
/* * 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); }