1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "apr_private.h"
18 #include "apr_arch_file_io.h"
19 #include "apr_file_io.h"
20 #include "apr_general.h"
21 #include "apr_strings.h"
22 #include "apr_portable.h"
23 #include "apr_thread_mutex.h"
29 #if APR_HAVE_SYS_STAT_H
32 #include "apr_arch_misc.h"
33 #include "apr_arch_inherit.h"
37 #if APR_HAS_UNICODE_FS
38 apr_status_t utf8_to_unicode_path(apr_wchar_t* retstr, apr_size_t retlen,
41 /* TODO: The computations could preconvert the string to determine
42 * the true size of the retstr, but that's a memory over speed
43 * tradeoff that isn't appropriate this early in development.
45 * Allocate the maximum string length based on leading 4
46 * characters of \\?\ (allowing nearly unlimited path lengths)
47 * plus the trailing null, then transform /'s into \\'s since
48 * the \\?\ form doesn't allow '/' path seperators.
50 * Note that the \\?\ form only works for local drive paths, and
51 * \\?\UNC\ is needed UNC paths.
53 apr_size_t srcremains = strlen(srcstr) + 1;
54 apr_wchar_t *t = retstr;
57 /* This is correct, we don't twist the filename if it is will
58 * definitely be shorter than 248 characters. It merits some
59 * performance testing to see if this has any effect, but there
60 * seem to be applications that get confused by the resulting
61 * Unicode \\?\ style file names, especially if they use argv[0]
62 * or call the Win32 API functions such as GetModuleName, etc.
63 * Not every application is prepared to handle such names.
65 * Note also this is shorter than MAX_PATH, as directory paths
66 * are actually limited to 248 characters.
68 * Note that a utf-8 name can never result in more wide chars
69 * than the original number of utf-8 narrow chars.
71 if (srcremains > 248) {
72 if (srcstr[1] == ':' && (srcstr[2] == '/' || srcstr[2] == '\\')) {
73 wcscpy (retstr, L"\\\\?\\");
77 else if ((srcstr[0] == '/' || srcstr[0] == '\\')
78 && (srcstr[1] == '/' || srcstr[1] == '\\')
79 && (srcstr[2] != '?')) {
80 /* Skip the slashes */
83 wcscpy (retstr, L"\\\\?\\UNC\\");
89 if ((rv = apr_conv_utf8_to_ucs2(srcstr, &srcremains, t, &retlen))) {
90 return (rv == APR_INCOMPLETE) ? APR_EINVAL : rv;
93 return APR_ENAMETOOLONG;
101 apr_status_t unicode_to_utf8_path(char* retstr, apr_size_t retlen,
102 const apr_wchar_t* srcstr)
104 /* Skip the leading 4 characters if the path begins \\?\, or substitute
105 * // for the \\?\UNC\ path prefix, allocating the maximum string
106 * length based on the remaining string, plus the trailing null.
107 * then transform \\'s back into /'s since the \\?\ form never
108 * allows '/' path seperators, and APR always uses '/'s.
110 apr_size_t srcremains = wcslen(srcstr) + 1;
113 if (srcstr[0] == L'\\' && srcstr[1] == L'\\' &&
114 srcstr[2] == L'?' && srcstr[3] == L'\\') {
115 if (srcstr[4] == L'U' && srcstr[5] == L'N' &&
116 srcstr[6] == L'C' && srcstr[7] == L'\\') {
130 if ((rv = apr_conv_ucs2_to_utf8(srcstr, &srcremains, t, &retlen))) {
134 return APR_ENAMETOOLONG;
140 void *res_name_from_filename(const char *file, int global, apr_pool_t *pool)
142 #if APR_HAS_UNICODE_FS
145 apr_wchar_t *wpre, *wfile, *ch;
146 apr_size_t n = strlen(file) + 1;
149 if (apr_os_level >= APR_WIN_2000) {
162 /* skip utf8 continuation bytes */
163 while ((*file & 0xC0) == 0x80) {
168 wfile = apr_palloc(pool, (r + n) * sizeof(apr_wchar_t));
171 if (apr_conv_utf8_to_ucs2(file, &n, wfile + r, &d)) {
174 for (ch = wfile + r; *ch; ++ch) {
175 if (*ch == ':' || *ch == '/' || *ch == '\\')
185 apr_size_t n = strlen(file) + 1;
187 #if !APR_HAS_UNICODE_FS
191 if (apr_os_level >= APR_WIN_2000) {
205 nfile = apr_palloc(pool, (r + n) * sizeof(apr_wchar_t));
206 memcpy(nfile, pre, r);
207 memcpy(nfile + r, file, n);
209 const apr_size_t r = 0;
214 nfile = apr_pmemdup(pool, file, n);
216 for (ch = nfile + r; *ch; ++ch) {
217 if (*ch == ':' || *ch == '/' || *ch == '\\')
225 #if APR_HAS_UNICODE_FS
226 static apr_status_t make_sparse_file(apr_file_t *file)
228 BY_HANDLE_FILE_INFORMATION info;
235 if (GetFileInformationByHandle(file->filehand, &info)
236 && (info.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE))
239 if (file->pOverlapped) {
240 file->pOverlapped->Offset = 0;
241 file->pOverlapped->OffsetHigh = 0;
244 if (DeviceIoControl(file->filehand, FSCTL_SET_SPARSE, NULL, 0, NULL, 0,
245 &bytesread, file->pOverlapped)) {
250 rv = apr_get_os_error();
252 if (rv == APR_FROM_OS_ERROR(ERROR_IO_PENDING))
255 res = WaitForSingleObject(file->pOverlapped->hEvent,
257 ? (DWORD)(file->timeout/1000)
258 : ((file->timeout == -1)
260 } while (res == WAIT_ABANDONED);
262 if (res != WAIT_OBJECT_0) {
263 CancelIo(file->filehand);
266 if (GetOverlappedResult(file->filehand, file->pOverlapped,
270 rv = apr_get_os_error();
277 apr_status_t file_cleanup(void *thefile)
279 apr_file_t *file = thefile;
280 apr_status_t flush_rv = APR_SUCCESS;
282 if (file->filehand != INVALID_HANDLE_VALUE) {
284 if (file->buffered) {
285 /* XXX: flush here is not mutex protected */
286 flush_rv = apr_file_flush((apr_file_t *)thefile);
289 /* In order to avoid later segfaults with handle 'reuse',
290 * we must protect against the case that a dup2'ed handle
291 * is being closed, and invalidate the corresponding StdHandle
292 * We also tell msvcrt when stdhandles are closed.
294 if (file->flags & APR_STD_FLAGS)
296 if ((file->flags & APR_STD_FLAGS) == APR_STDERR_FLAG) {
298 SetStdHandle(STD_ERROR_HANDLE, INVALID_HANDLE_VALUE);
300 else if ((file->flags & APR_STD_FLAGS) == APR_STDOUT_FLAG) {
302 SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE);
304 else if ((file->flags & APR_STD_FLAGS) == APR_STDIN_FLAG) {
306 SetStdHandle(STD_INPUT_HANDLE, INVALID_HANDLE_VALUE);
310 CloseHandle(file->filehand);
312 file->filehand = INVALID_HANDLE_VALUE;
314 if (file->pOverlapped && file->pOverlapped->hEvent) {
315 CloseHandle(file->pOverlapped->hEvent);
316 file->pOverlapped = NULL;
321 APR_DECLARE(apr_status_t) apr_file_open(apr_file_t **new, const char *fname,
322 apr_int32_t flag, apr_fileperms_t perm,
325 HANDLE handle = INVALID_HANDLE_VALUE;
327 DWORD createflags = 0;
328 DWORD attributes = 0;
329 DWORD sharemode = FILE_SHARE_READ | FILE_SHARE_WRITE;
332 if (flag & APR_FOPEN_NONBLOCK) {
335 if (flag & APR_FOPEN_READ) {
336 oflags |= GENERIC_READ;
338 if (flag & APR_FOPEN_WRITE) {
339 oflags |= GENERIC_WRITE;
341 if (flag & APR_WRITEATTRS) {
342 oflags |= FILE_WRITE_ATTRIBUTES;
345 if (apr_os_level >= APR_WIN_NT)
346 sharemode |= FILE_SHARE_DELETE;
348 if (flag & APR_FOPEN_CREATE) {
349 if (flag & APR_FOPEN_EXCL) {
350 /* only create new if file does not already exist */
351 createflags = CREATE_NEW;
352 } else if (flag & APR_FOPEN_TRUNCATE) {
353 /* truncate existing file or create new */
354 createflags = CREATE_ALWAYS;
356 /* open existing but create if necessary */
357 createflags = OPEN_ALWAYS;
359 } else if (flag & APR_FOPEN_TRUNCATE) {
360 /* only truncate if file already exists */
361 createflags = TRUNCATE_EXISTING;
363 /* only open if file already exists */
364 createflags = OPEN_EXISTING;
367 if ((flag & APR_FOPEN_EXCL) && !(flag & APR_FOPEN_CREATE)) {
371 if (flag & APR_FOPEN_DELONCLOSE) {
372 attributes |= FILE_FLAG_DELETE_ON_CLOSE;
375 if (flag & APR_OPENLINK) {
376 attributes |= FILE_FLAG_OPEN_REPARSE_POINT;
379 /* Without READ or WRITE, we fail unless apr called apr_file_open
380 * internally with the private APR_OPENINFO flag.
382 * With the APR_OPENINFO flag on NT, use the option flag
383 * FILE_FLAG_BACKUP_SEMANTICS to allow us to open directories.
384 * See the static resolve_ident() fn in file_io/win32/filestat.c
386 if (!(flag & (APR_FOPEN_READ | APR_FOPEN_WRITE))) {
387 if (flag & APR_OPENINFO) {
388 if (apr_os_level >= APR_WIN_NT) {
389 attributes |= FILE_FLAG_BACKUP_SEMANTICS;
395 if (flag & APR_READCONTROL)
396 oflags |= READ_CONTROL;
399 if (flag & APR_FOPEN_XTHREAD) {
400 /* This win32 specific feature is required
401 * to allow multiple threads to work with the file.
403 attributes |= FILE_FLAG_OVERLAPPED;
406 #if APR_HAS_UNICODE_FS
409 apr_wchar_t wfname[APR_PATH_MAX];
411 if (flag & APR_FOPEN_SENDFILE_ENABLED) {
412 /* This feature is required to enable sendfile operations
413 * against the file on Win32. Also implies APR_FOPEN_XTHREAD.
415 flag |= APR_FOPEN_XTHREAD;
416 attributes |= FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED;
419 if ((rv = utf8_to_unicode_path(wfname, sizeof(wfname)
420 / sizeof(apr_wchar_t), fname)))
422 handle = CreateFileW(wfname, oflags, sharemode,
423 NULL, createflags, attributes, 0);
427 ELSE_WIN_OS_IS_ANSI {
428 handle = CreateFileA(fname, oflags, sharemode,
429 NULL, createflags, attributes, 0);
430 /* This feature is not supported on this platform. */
431 flag &= ~APR_FOPEN_SENDFILE_ENABLED;
434 if (handle == INVALID_HANDLE_VALUE) {
435 return apr_get_os_error();
438 (*new) = (apr_file_t *)apr_pcalloc(pool, sizeof(apr_file_t));
440 (*new)->filehand = handle;
441 (*new)->fname = apr_pstrdup(pool, fname);
442 (*new)->flags = flag;
443 (*new)->timeout = -1;
444 (*new)->ungetchar = -1;
446 if (flag & APR_FOPEN_APPEND) {
448 SetFilePointer((*new)->filehand, 0, NULL, FILE_END);
450 if (flag & APR_FOPEN_BUFFERED) {
451 (*new)->buffered = 1;
452 (*new)->buffer = apr_palloc(pool, APR_FILE_DEFAULT_BUFSIZE);
453 (*new)->bufsize = APR_FILE_DEFAULT_BUFSIZE;
455 /* Need the mutex to handled buffered and O_APPEND style file i/o */
456 if ((*new)->buffered || (*new)->append) {
457 rv = apr_thread_mutex_create(&(*new)->mutex,
458 APR_THREAD_MUTEX_DEFAULT, pool);
460 if (file_cleanup(*new) == APR_SUCCESS) {
461 apr_pool_cleanup_kill(pool, *new, file_cleanup);
467 #if APR_HAS_UNICODE_FS
468 if ((apr_os_level >= APR_WIN_2000) && ((*new)->flags & APR_FOPEN_SPARSE)) {
469 if ((rv = make_sparse_file(*new)) != APR_SUCCESS)
470 /* The great mystery; do we close the file and return an error?
471 * Do we add a new APR_INCOMPLETE style error saying opened, but
472 * NOTSPARSE? For now let's simply mark the file as not-sparse.
474 (*new)->flags &= ~APR_FOPEN_SPARSE;
478 /* This feature is not supported on this platform. */
479 (*new)->flags &= ~APR_FOPEN_SPARSE;
481 #if APR_FILES_AS_SOCKETS
482 /* Create a pollset with room for one descriptor. */
483 /* ### check return codes */
484 (void) apr_pollset_create(&(*new)->pollset, 1, pool, 0);
486 if (!(flag & APR_FOPEN_NOCLEANUP)) {
487 apr_pool_cleanup_register((*new)->pool, (void *)(*new), file_cleanup,
488 apr_pool_cleanup_null);
493 APR_DECLARE(apr_status_t) apr_file_close(apr_file_t *file)
496 if ((stat = file_cleanup(file)) == APR_SUCCESS) {
497 apr_pool_cleanup_kill(file->pool, file, file_cleanup);
500 apr_thread_mutex_destroy(file->mutex);
508 APR_DECLARE(apr_status_t) apr_file_remove(const char *path, apr_pool_t *pool)
510 #if APR_HAS_UNICODE_FS
513 apr_wchar_t wpath[APR_PATH_MAX];
515 if ((rv = utf8_to_unicode_path(wpath, sizeof(wpath)
516 / sizeof(apr_wchar_t), path))) {
519 if (DeleteFileW(wpath))
525 if (DeleteFile(path))
528 return apr_get_os_error();
531 APR_DECLARE(apr_status_t) apr_file_rename(const char *frompath,
537 #if APR_HAS_UNICODE_FS
538 apr_wchar_t wfrompath[APR_PATH_MAX], wtopath[APR_PATH_MAX];
540 if ((rv = utf8_to_unicode_path(wfrompath,
541 sizeof(wfrompath) / sizeof(apr_wchar_t),
545 if ((rv = utf8_to_unicode_path(wtopath,
546 sizeof(wtopath) / sizeof(apr_wchar_t),
551 if (MoveFileExW(wfrompath, wtopath, MOVEFILE_REPLACE_EXISTING |
552 MOVEFILE_COPY_ALLOWED))
554 if (MoveFileW(wfrompath, wtopath))
558 if (MoveFileEx(frompath, topath, MOVEFILE_REPLACE_EXISTING |
559 MOVEFILE_COPY_ALLOWED))
566 /* Windows 95 and 98 do not support MoveFileEx, so we'll use
567 * the old MoveFile function. However, MoveFile requires that
568 * the new file not already exist...so we have to delete that
569 * file if it does. Perhaps we should back up the to-be-deleted
570 * file in case something happens?
572 HANDLE handle = INVALID_HANDLE_VALUE;
574 if ((handle = CreateFile(topath, GENERIC_WRITE, 0, 0,
575 OPEN_EXISTING, 0, 0 )) != INVALID_HANDLE_VALUE )
578 if (!DeleteFile(topath))
579 return apr_get_os_error();
581 if (MoveFile(frompath, topath))
585 return apr_get_os_error();
588 APR_DECLARE(apr_status_t) apr_file_link(const char *from_path,
591 apr_status_t rv = APR_SUCCESS;
593 #if APR_HAS_UNICODE_FS
596 apr_wchar_t wfrom_path[APR_PATH_MAX];
597 apr_wchar_t wto_path[APR_PATH_MAX];
599 if ((rv = utf8_to_unicode_path(wfrom_path,
600 sizeof(wfrom_path) / sizeof(apr_wchar_t),
603 if ((rv = utf8_to_unicode_path(wto_path,
604 sizeof(wto_path) / sizeof(apr_wchar_t),
608 if (!CreateHardLinkW(wto_path, wfrom_path, NULL))
609 return apr_get_os_error();
613 ELSE_WIN_OS_IS_ANSI {
614 if (!CreateHardLinkA(to_path, from_path, NULL))
615 return apr_get_os_error();
621 APR_DECLARE(apr_status_t) apr_os_file_get(apr_os_file_t *thefile,
624 *thefile = file->filehand;
628 APR_DECLARE(apr_status_t) apr_os_file_put(apr_file_t **file,
629 apr_os_file_t *thefile,
633 (*file) = apr_pcalloc(pool, sizeof(apr_file_t));
634 (*file)->pool = pool;
635 (*file)->filehand = *thefile;
636 (*file)->ungetchar = -1; /* no char avail */
637 (*file)->timeout = -1;
638 (*file)->flags = flags;
640 if (flags & APR_FOPEN_APPEND) {
643 if (flags & APR_FOPEN_BUFFERED) {
644 (*file)->buffered = 1;
645 (*file)->buffer = apr_palloc(pool, APR_FILE_DEFAULT_BUFSIZE);
646 (*file)->bufsize = APR_FILE_DEFAULT_BUFSIZE;
649 if ((*file)->append || (*file)->buffered) {
651 rv = apr_thread_mutex_create(&(*file)->mutex,
652 APR_THREAD_MUTEX_DEFAULT, pool);
658 #if APR_FILES_AS_SOCKETS
659 /* Create a pollset with room for one descriptor. */
660 /* ### check return codes */
661 (void) apr_pollset_create(&(*file)->pollset, 1, pool, 0);
663 /* Should we be testing if thefile is a handle to
664 * a PIPE and set up the mechanics appropriately?
671 APR_DECLARE(apr_status_t) apr_file_eof(apr_file_t *fptr)
673 if (fptr->eof_hit == 1) {
679 APR_DECLARE(apr_status_t) apr_file_open_flags_stderr(apr_file_t **thefile,
686 apr_os_file_t file_handle;
688 apr_set_os_error(APR_SUCCESS);
689 file_handle = GetStdHandle(STD_ERROR_HANDLE);
691 file_handle = INVALID_HANDLE_VALUE;
693 return apr_os_file_put(thefile, &file_handle,
694 flags | APR_FOPEN_WRITE | APR_STDERR_FLAG, pool);
698 APR_DECLARE(apr_status_t) apr_file_open_flags_stdout(apr_file_t **thefile,
705 apr_os_file_t file_handle;
707 apr_set_os_error(APR_SUCCESS);
708 file_handle = GetStdHandle(STD_OUTPUT_HANDLE);
710 file_handle = INVALID_HANDLE_VALUE;
712 return apr_os_file_put(thefile, &file_handle,
713 flags | APR_FOPEN_WRITE | APR_STDOUT_FLAG, pool);
717 APR_DECLARE(apr_status_t) apr_file_open_flags_stdin(apr_file_t **thefile,
724 apr_os_file_t file_handle;
726 apr_set_os_error(APR_SUCCESS);
727 file_handle = GetStdHandle(STD_INPUT_HANDLE);
729 file_handle = INVALID_HANDLE_VALUE;
731 return apr_os_file_put(thefile, &file_handle,
732 flags | APR_FOPEN_READ | APR_STDIN_FLAG, pool);
736 APR_DECLARE(apr_status_t) apr_file_open_stderr(apr_file_t **thefile, apr_pool_t *pool)
738 return apr_file_open_flags_stderr(thefile, 0, pool);
741 APR_DECLARE(apr_status_t) apr_file_open_stdout(apr_file_t **thefile, apr_pool_t *pool)
743 return apr_file_open_flags_stdout(thefile, 0, pool);
746 APR_DECLARE(apr_status_t) apr_file_open_stdin(apr_file_t **thefile, apr_pool_t *pool)
748 return apr_file_open_flags_stdin(thefile, 0, pool);
751 APR_POOL_IMPLEMENT_ACCESSOR(file);
753 APR_IMPLEMENT_INHERIT_SET(file, flags, pool, file_cleanup)
755 APR_IMPLEMENT_INHERIT_UNSET(file, flags, pool, file_cleanup)