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 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 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. 15 */ 16 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" 24#if APR_HAVE_ERRNO_H 25#include <errno.h> 26#endif 27#include <winbase.h> 28#include <string.h> 29#if APR_HAVE_SYS_STAT_H 30#include <sys/stat.h> 31#endif 32#include "apr_arch_misc.h" 33#include "apr_arch_inherit.h" 34#include <io.h> 35#include <winioctl.h> 36 37#if APR_HAS_UNICODE_FS 38apr_status_t utf8_to_unicode_path(apr_wchar_t* retstr, apr_size_t retlen, 39 const char* srcstr) 40{ 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. 44 * 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. 49 * 50 * Note that the \\?\ form only works for local drive paths, and 51 * \\?\UNC\ is needed UNC paths. 52 */ 53 apr_size_t srcremains = strlen(srcstr) + 1; 54 apr_wchar_t *t = retstr; 55 apr_status_t rv; 56 57 /* This is correct, we don't twist the filename if it is will 58 * definately 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. 64 * 65 * Note also this is shorter than MAX_PATH, as directory paths 66 * are actually limited to 248 characters. 67 * 68 * Note that a utf-8 name can never result in more wide chars 69 * than the original number of utf-8 narrow chars. 70 */ 71 if (srcremains > 248) { 72 if (srcstr[1] == ':' && (srcstr[2] == '/' || srcstr[2] == '\\')) { 73 wcscpy (retstr, L"\\\\?\\"); 74 retlen -= 4; 75 t += 4; 76 } 77 else if ((srcstr[0] == '/' || srcstr[0] == '\\') 78 && (srcstr[1] == '/' || srcstr[1] == '\\') 79 && (srcstr[2] != '?')) { 80 /* Skip the slashes */ 81 srcstr += 2; 82 srcremains -= 2; 83 wcscpy (retstr, L"\\\\?\\UNC\\"); 84 retlen -= 8; 85 t += 8; 86 } 87 } 88 89 if ((rv = apr_conv_utf8_to_ucs2(srcstr, &srcremains, t, &retlen))) { 90 return (rv == APR_INCOMPLETE) ? APR_EINVAL : rv; 91 } 92 if (srcremains) { 93 return APR_ENAMETOOLONG; 94 } 95 for (; *t; ++t) 96 if (*t == L'/') 97 *t = L'\\'; 98 return APR_SUCCESS; 99} 100 101apr_status_t unicode_to_utf8_path(char* retstr, apr_size_t retlen, 102 const apr_wchar_t* srcstr) 103{ 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. 109 */ 110 apr_size_t srcremains = wcslen(srcstr) + 1; 111 apr_status_t rv; 112 char *t = retstr; 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'\\') { 117 srcremains -= 8; 118 srcstr += 8; 119 retstr[0] = '\\'; 120 retstr[1] = '\\'; 121 retlen -= 2; 122 t += 2; 123 } 124 else { 125 srcremains -= 4; 126 srcstr += 4; 127 } 128 } 129 130 if ((rv = apr_conv_ucs2_to_utf8(srcstr, &srcremains, t, &retlen))) { 131 return rv; 132 } 133 if (srcremains) { 134 return APR_ENAMETOOLONG; 135 } 136 return APR_SUCCESS; 137} 138#endif 139 140void *res_name_from_filename(const char *file, int global, apr_pool_t *pool) 141{ 142#if APR_HAS_UNICODE_FS 143 IF_WIN_OS_IS_UNICODE 144 { 145 apr_wchar_t *wpre, *wfile, *ch; 146 apr_size_t n = strlen(file) + 1; 147 apr_size_t r, d; 148 149 if (apr_os_level >= APR_WIN_2000) { 150 if (global) 151 wpre = L"Global\\"; 152 else 153 wpre = L"Local\\"; 154 } 155 else 156 wpre = L""; 157 r = wcslen(wpre); 158 159 if (n > 256 - r) { 160 file += n - 256 - r; 161 n = 256; 162 /* skip utf8 continuation bytes */ 163 while ((*file & 0xC0) == 0x80) { 164 ++file; 165 --n; 166 } 167 } 168 wfile = apr_palloc(pool, (r + n) * sizeof(apr_wchar_t)); 169 wcscpy(wfile, wpre); 170 d = n; 171 if (apr_conv_utf8_to_ucs2(file, &n, wfile + r, &d)) { 172 return NULL; 173 } 174 for (ch = wfile + r; *ch; ++ch) { 175 if (*ch == ':' || *ch == '/' || *ch == '\\') 176 *ch = '_'; 177 } 178 return wfile; 179 } 180#endif 181#if APR_HAS_ANSI_FS 182 ELSE_WIN_OS_IS_ANSI 183 { 184 char *nfile, *ch; 185 apr_size_t n = strlen(file) + 1; 186 187#if !APR_HAS_UNICODE_FS 188 apr_size_t r, d; 189 char *pre; 190 191 if (apr_os_level >= APR_WIN_2000) { 192 if (global) 193 pre = "Global\\"; 194 else 195 pre = "Local\\"; 196 } 197 else 198 pre = ""; 199 r = strlen(pre); 200 201 if (n > 256 - r) { 202 file += n - 256 - r; 203 n = 256; 204 } 205 nfile = apr_palloc(pool, (r + n) * sizeof(apr_wchar_t)); 206 memcpy(nfile, pre, r); 207 memcpy(nfile + r, file, n); 208#else 209 const apr_size_t r = 0; 210 if (n > 256) { 211 file += n - 256; 212 n = 256; 213 } 214 nfile = apr_pmemdup(pool, file, n); 215#endif 216 for (ch = nfile + r; *ch; ++ch) { 217 if (*ch == ':' || *ch == '/' || *ch == '\\') 218 *ch = '_'; 219 } 220 return nfile; 221 } 222#endif 223} 224 225#if APR_HAS_UNICODE_FS 226static apr_status_t make_sparse_file(apr_file_t *file) 227{ 228 BY_HANDLE_FILE_INFORMATION info; 229 apr_status_t rv; 230 DWORD bytesread = 0; 231 DWORD res; 232 233 /* test */ 234 235 if (GetFileInformationByHandle(file->filehand, &info) 236 && (info.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE)) 237 return APR_SUCCESS; 238 239 if (file->pOverlapped) { 240 file->pOverlapped->Offset = 0; 241 file->pOverlapped->OffsetHigh = 0; 242 } 243 244 if (DeviceIoControl(file->filehand, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, 245 &bytesread, file->pOverlapped)) { 246 rv = APR_SUCCESS; 247 } 248 else 249 { 250 rv = apr_get_os_error(); 251 252 if (rv == APR_FROM_OS_ERROR(ERROR_IO_PENDING)) 253 { 254 do { 255 res = WaitForSingleObject(file->pOverlapped->hEvent, 256 (file->timeout > 0) 257 ? (DWORD)(file->timeout/1000) 258 : ((file->timeout == -1) 259 ? INFINITE : 0)); 260 } while (res == WAIT_ABANDONED); 261 262 if (res != WAIT_OBJECT_0) { 263 CancelIo(file->filehand); 264 } 265 266 if (GetOverlappedResult(file->filehand, file->pOverlapped, 267 &bytesread, TRUE)) 268 rv = APR_SUCCESS; 269 else 270 rv = apr_get_os_error(); 271 } 272 } 273 return rv; 274} 275#endif 276 277apr_status_t file_cleanup(void *thefile) 278{ 279 apr_file_t *file = thefile; 280 apr_status_t flush_rv = APR_SUCCESS; 281 282 if (file->filehand != INVALID_HANDLE_VALUE) { 283 284 if (file->buffered) { 285 /* XXX: flush here is not mutex protected */ 286 flush_rv = apr_file_flush((apr_file_t *)thefile); 287 } 288 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. 293 */ 294 if (file->flags & APR_STD_FLAGS) 295 { 296 if ((file->flags & APR_STD_FLAGS) == APR_STDERR_FLAG) { 297 _close(2); 298 SetStdHandle(STD_ERROR_HANDLE, INVALID_HANDLE_VALUE); 299 } 300 else if ((file->flags & APR_STD_FLAGS) == APR_STDOUT_FLAG) { 301 _close(1); 302 SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE); 303 } 304 else if ((file->flags & APR_STD_FLAGS) == APR_STDIN_FLAG) { 305 _close(0); 306 SetStdHandle(STD_INPUT_HANDLE, INVALID_HANDLE_VALUE); 307 } 308 } 309 else 310 CloseHandle(file->filehand); 311 312 file->filehand = INVALID_HANDLE_VALUE; 313 } 314 if (file->pOverlapped && file->pOverlapped->hEvent) { 315 CloseHandle(file->pOverlapped->hEvent); 316 file->pOverlapped = NULL; 317 } 318 return flush_rv; 319} 320 321APR_DECLARE(apr_status_t) apr_file_open(apr_file_t **new, const char *fname, 322 apr_int32_t flag, apr_fileperms_t perm, 323 apr_pool_t *pool) 324{ 325 HANDLE handle = INVALID_HANDLE_VALUE; 326 DWORD oflags = 0; 327 DWORD createflags = 0; 328 DWORD attributes = 0; 329 DWORD sharemode = FILE_SHARE_READ | FILE_SHARE_WRITE; 330 apr_status_t rv; 331 332 if (flag & APR_FOPEN_READ) { 333 oflags |= GENERIC_READ; 334 } 335 if (flag & APR_FOPEN_WRITE) { 336 oflags |= GENERIC_WRITE; 337 } 338 if (flag & APR_WRITEATTRS) { 339 oflags |= FILE_WRITE_ATTRIBUTES; 340 } 341 342 if (apr_os_level >= APR_WIN_NT) 343 sharemode |= FILE_SHARE_DELETE; 344 345 if (flag & APR_FOPEN_CREATE) { 346 if (flag & APR_FOPEN_EXCL) { 347 /* only create new if file does not already exist */ 348 createflags = CREATE_NEW; 349 } else if (flag & APR_FOPEN_TRUNCATE) { 350 /* truncate existing file or create new */ 351 createflags = CREATE_ALWAYS; 352 } else { 353 /* open existing but create if necessary */ 354 createflags = OPEN_ALWAYS; 355 } 356 } else if (flag & APR_FOPEN_TRUNCATE) { 357 /* only truncate if file already exists */ 358 createflags = TRUNCATE_EXISTING; 359 } else { 360 /* only open if file already exists */ 361 createflags = OPEN_EXISTING; 362 } 363 364 if ((flag & APR_FOPEN_EXCL) && !(flag & APR_FOPEN_CREATE)) { 365 return APR_EACCES; 366 } 367 368 if (flag & APR_FOPEN_DELONCLOSE) { 369 attributes |= FILE_FLAG_DELETE_ON_CLOSE; 370 } 371 372 if (flag & APR_OPENLINK) { 373 attributes |= FILE_FLAG_OPEN_REPARSE_POINT; 374 } 375 376 /* Without READ or WRITE, we fail unless apr called apr_file_open 377 * internally with the private APR_OPENINFO flag. 378 * 379 * With the APR_OPENINFO flag on NT, use the option flag 380 * FILE_FLAG_BACKUP_SEMANTICS to allow us to open directories. 381 * See the static resolve_ident() fn in file_io/win32/filestat.c 382 */ 383 if (!(flag & (APR_FOPEN_READ | APR_FOPEN_WRITE))) { 384 if (flag & APR_OPENINFO) { 385 if (apr_os_level >= APR_WIN_NT) { 386 attributes |= FILE_FLAG_BACKUP_SEMANTICS; 387 } 388 } 389 else { 390 return APR_EACCES; 391 } 392 if (flag & APR_READCONTROL) 393 oflags |= READ_CONTROL; 394 } 395 396 if (flag & APR_FOPEN_XTHREAD) { 397 /* This win32 specific feature is required 398 * to allow multiple threads to work with the file. 399 */ 400 attributes |= FILE_FLAG_OVERLAPPED; 401 } 402 403#if APR_HAS_UNICODE_FS 404 IF_WIN_OS_IS_UNICODE 405 { 406 apr_wchar_t wfname[APR_PATH_MAX]; 407 408 if (flag & APR_FOPEN_SENDFILE_ENABLED) { 409 /* This feature is required to enable sendfile operations 410 * against the file on Win32. Also implies APR_FOPEN_XTHREAD. 411 */ 412 flag |= APR_FOPEN_XTHREAD; 413 attributes |= FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED; 414 } 415 416 if ((rv = utf8_to_unicode_path(wfname, sizeof(wfname) 417 / sizeof(apr_wchar_t), fname))) 418 return rv; 419 handle = CreateFileW(wfname, oflags, sharemode, 420 NULL, createflags, attributes, 0); 421 } 422#endif 423#if APR_HAS_ANSI_FS 424 ELSE_WIN_OS_IS_ANSI { 425 handle = CreateFileA(fname, oflags, sharemode, 426 NULL, createflags, attributes, 0); 427 /* This feature is not supported on this platform. */ 428 flag &= ~APR_FOPEN_SENDFILE_ENABLED; 429 } 430#endif 431 if (handle == INVALID_HANDLE_VALUE) { 432 return apr_get_os_error(); 433 } 434 435 (*new) = (apr_file_t *)apr_pcalloc(pool, sizeof(apr_file_t)); 436 (*new)->pool = pool; 437 (*new)->filehand = handle; 438 (*new)->fname = apr_pstrdup(pool, fname); 439 (*new)->flags = flag; 440 (*new)->timeout = -1; 441 (*new)->ungetchar = -1; 442 443 if (flag & APR_FOPEN_APPEND) { 444 (*new)->append = 1; 445 SetFilePointer((*new)->filehand, 0, NULL, FILE_END); 446 } 447 if (flag & APR_FOPEN_BUFFERED) { 448 (*new)->buffered = 1; 449 (*new)->buffer = apr_palloc(pool, APR_FILE_DEFAULT_BUFSIZE); 450 (*new)->bufsize = APR_FILE_DEFAULT_BUFSIZE; 451 } 452 /* Need the mutex to handled buffered and O_APPEND style file i/o */ 453 if ((*new)->buffered || (*new)->append) { 454 rv = apr_thread_mutex_create(&(*new)->mutex, 455 APR_THREAD_MUTEX_DEFAULT, pool); 456 if (rv) { 457 if (file_cleanup(*new) == APR_SUCCESS) { 458 apr_pool_cleanup_kill(pool, *new, file_cleanup); 459 } 460 return rv; 461 } 462 } 463 464#if APR_HAS_UNICODE_FS 465 if ((apr_os_level >= APR_WIN_2000) && ((*new)->flags & APR_FOPEN_SPARSE)) { 466 if ((rv = make_sparse_file(*new)) != APR_SUCCESS) 467 /* The great mystery; do we close the file and return an error? 468 * Do we add a new APR_INCOMPLETE style error saying opened, but 469 * NOTSPARSE? For now let's simply mark the file as not-sparse. 470 */ 471 (*new)->flags &= ~APR_FOPEN_SPARSE; 472 } 473 else 474#endif 475 /* This feature is not supported on this platform. */ 476 (*new)->flags &= ~APR_FOPEN_SPARSE; 477 478 /* Create a pollset with room for one descriptor. */ 479 /* ### check return codes */ 480 (void) apr_pollset_create(&(*new)->pollset, 1, pool, 0); 481 482 if (!(flag & APR_FOPEN_NOCLEANUP)) { 483 apr_pool_cleanup_register((*new)->pool, (void *)(*new), file_cleanup, 484 apr_pool_cleanup_null); 485 } 486 return APR_SUCCESS; 487} 488 489APR_DECLARE(apr_status_t) apr_file_close(apr_file_t *file) 490{ 491 apr_status_t stat; 492 if ((stat = file_cleanup(file)) == APR_SUCCESS) { 493 apr_pool_cleanup_kill(file->pool, file, file_cleanup); 494 495 if (file->mutex) { 496 apr_thread_mutex_destroy(file->mutex); 497 } 498 499 return APR_SUCCESS; 500 } 501 return stat; 502} 503 504APR_DECLARE(apr_status_t) apr_file_remove(const char *path, apr_pool_t *pool) 505{ 506#if APR_HAS_UNICODE_FS 507 IF_WIN_OS_IS_UNICODE 508 { 509 apr_wchar_t wpath[APR_PATH_MAX]; 510 apr_status_t rv; 511 if ((rv = utf8_to_unicode_path(wpath, sizeof(wpath) 512 / sizeof(apr_wchar_t), path))) { 513 return rv; 514 } 515 if (DeleteFileW(wpath)) 516 return APR_SUCCESS; 517 } 518#endif 519#if APR_HAS_ANSI_FS 520 ELSE_WIN_OS_IS_ANSI 521 if (DeleteFile(path)) 522 return APR_SUCCESS; 523#endif 524 return apr_get_os_error(); 525} 526 527APR_DECLARE(apr_status_t) apr_file_rename(const char *frompath, 528 const char *topath, 529 apr_pool_t *pool) 530{ 531 IF_WIN_OS_IS_UNICODE 532 { 533#if APR_HAS_UNICODE_FS 534 apr_wchar_t wfrompath[APR_PATH_MAX], wtopath[APR_PATH_MAX]; 535 apr_status_t rv; 536 if ((rv = utf8_to_unicode_path(wfrompath, 537 sizeof(wfrompath) / sizeof(apr_wchar_t), 538 frompath))) { 539 return rv; 540 } 541 if ((rv = utf8_to_unicode_path(wtopath, 542 sizeof(wtopath) / sizeof(apr_wchar_t), 543 topath))) { 544 return rv; 545 } 546#ifndef _WIN32_WCE 547 if (MoveFileExW(wfrompath, wtopath, MOVEFILE_REPLACE_EXISTING | 548 MOVEFILE_COPY_ALLOWED)) 549#else 550 if (MoveFileW(wfrompath, wtopath)) 551#endif 552 return APR_SUCCESS; 553#else 554 if (MoveFileEx(frompath, topath, MOVEFILE_REPLACE_EXISTING | 555 MOVEFILE_COPY_ALLOWED)) 556 return APR_SUCCESS; 557#endif 558 } 559#if APR_HAS_ANSI_FS 560 ELSE_WIN_OS_IS_ANSI 561 { 562 /* Windows 95 and 98 do not support MoveFileEx, so we'll use 563 * the old MoveFile function. However, MoveFile requires that 564 * the new file not already exist...so we have to delete that 565 * file if it does. Perhaps we should back up the to-be-deleted 566 * file in case something happens? 567 */ 568 HANDLE handle = INVALID_HANDLE_VALUE; 569 570 if ((handle = CreateFile(topath, GENERIC_WRITE, 0, 0, 571 OPEN_EXISTING, 0, 0 )) != INVALID_HANDLE_VALUE ) 572 { 573 CloseHandle(handle); 574 if (!DeleteFile(topath)) 575 return apr_get_os_error(); 576 } 577 if (MoveFile(frompath, topath)) 578 return APR_SUCCESS; 579 } 580#endif 581 return apr_get_os_error(); 582} 583 584APR_DECLARE(apr_status_t) apr_file_link(const char *from_path, 585 const char *to_path) 586{ 587 apr_status_t rv = APR_SUCCESS; 588 589#if APR_HAS_UNICODE_FS 590 IF_WIN_OS_IS_UNICODE 591 { 592 apr_wchar_t wfrom_path[APR_PATH_MAX]; 593 apr_wchar_t wto_path[APR_PATH_MAX]; 594 595 if ((rv = utf8_to_unicode_path(wfrom_path, 596 sizeof(wfrom_path) / sizeof(apr_wchar_t), 597 from_path))) 598 return rv; 599 if ((rv = utf8_to_unicode_path(wto_path, 600 sizeof(wto_path) / sizeof(apr_wchar_t), 601 to_path))) 602 return rv; 603 604 if (!CreateHardLinkW(wto_path, wfrom_path, NULL)) 605 return apr_get_os_error(); 606 } 607#endif 608#if APR_HAS_ANSI_FS 609 ELSE_WIN_OS_IS_ANSI { 610 if (!CreateHardLinkA(to_path, from_path, NULL)) 611 return apr_get_os_error(); 612 } 613#endif 614 return rv; 615} 616 617APR_DECLARE(apr_status_t) apr_os_file_get(apr_os_file_t *thefile, 618 apr_file_t *file) 619{ 620 *thefile = file->filehand; 621 return APR_SUCCESS; 622} 623 624APR_DECLARE(apr_status_t) apr_os_file_put(apr_file_t **file, 625 apr_os_file_t *thefile, 626 apr_int32_t flags, 627 apr_pool_t *pool) 628{ 629 (*file) = apr_pcalloc(pool, sizeof(apr_file_t)); 630 (*file)->pool = pool; 631 (*file)->filehand = *thefile; 632 (*file)->ungetchar = -1; /* no char avail */ 633 (*file)->timeout = -1; 634 (*file)->flags = flags; 635 636 if (flags & APR_FOPEN_APPEND) { 637 (*file)->append = 1; 638 } 639 if (flags & APR_FOPEN_BUFFERED) { 640 (*file)->buffered = 1; 641 (*file)->buffer = apr_palloc(pool, APR_FILE_DEFAULT_BUFSIZE); 642 (*file)->bufsize = APR_FILE_DEFAULT_BUFSIZE; 643 } 644 645 if ((*file)->append || (*file)->buffered) { 646 apr_status_t rv; 647 rv = apr_thread_mutex_create(&(*file)->mutex, 648 APR_THREAD_MUTEX_DEFAULT, pool); 649 if (rv) { 650 if (file_cleanup(*file) == APR_SUCCESS) { 651 apr_pool_cleanup_kill(pool, *file, file_cleanup); 652 } 653 return rv; 654 } 655 } 656 657 /* Create a pollset with room for one descriptor. */ 658 /* ### check return codes */ 659 (void) apr_pollset_create(&(*file)->pollset, 1, pool, 0); 660 661 /* Should we be testing if thefile is a handle to 662 * a PIPE and set up the mechanics appropriately? 663 * 664 * (*file)->pipe; 665 */ 666 return APR_SUCCESS; 667} 668 669APR_DECLARE(apr_status_t) apr_file_eof(apr_file_t *fptr) 670{ 671 if (fptr->eof_hit == 1) { 672 return APR_EOF; 673 } 674 return APR_SUCCESS; 675} 676 677APR_DECLARE(apr_status_t) apr_file_open_flags_stderr(apr_file_t **thefile, 678 apr_int32_t flags, 679 apr_pool_t *pool) 680{ 681#ifdef _WIN32_WCE 682 return APR_ENOTIMPL; 683#else 684 apr_os_file_t file_handle; 685 686 apr_set_os_error(APR_SUCCESS); 687 file_handle = GetStdHandle(STD_ERROR_HANDLE); 688 if (!file_handle) 689 file_handle = INVALID_HANDLE_VALUE; 690 691 return apr_os_file_put(thefile, &file_handle, 692 flags | APR_FOPEN_WRITE | APR_STDERR_FLAG, pool); 693#endif 694} 695 696APR_DECLARE(apr_status_t) apr_file_open_flags_stdout(apr_file_t **thefile, 697 apr_int32_t flags, 698 apr_pool_t *pool) 699{ 700#ifdef _WIN32_WCE 701 return APR_ENOTIMPL; 702#else 703 apr_os_file_t file_handle; 704 705 apr_set_os_error(APR_SUCCESS); 706 file_handle = GetStdHandle(STD_OUTPUT_HANDLE); 707 if (!file_handle) 708 file_handle = INVALID_HANDLE_VALUE; 709 710 return apr_os_file_put(thefile, &file_handle, 711 flags | APR_FOPEN_WRITE | APR_STDOUT_FLAG, pool); 712#endif 713} 714 715APR_DECLARE(apr_status_t) apr_file_open_flags_stdin(apr_file_t **thefile, 716 apr_int32_t flags, 717 apr_pool_t *pool) 718{ 719#ifdef _WIN32_WCE 720 return APR_ENOTIMPL; 721#else 722 apr_os_file_t file_handle; 723 724 apr_set_os_error(APR_SUCCESS); 725 file_handle = GetStdHandle(STD_INPUT_HANDLE); 726 if (!file_handle) 727 file_handle = INVALID_HANDLE_VALUE; 728 729 return apr_os_file_put(thefile, &file_handle, 730 flags | APR_FOPEN_READ | APR_STDIN_FLAG, pool); 731#endif 732} 733 734APR_DECLARE(apr_status_t) apr_file_open_stderr(apr_file_t **thefile, apr_pool_t *pool) 735{ 736 return apr_file_open_flags_stderr(thefile, 0, pool); 737} 738 739APR_DECLARE(apr_status_t) apr_file_open_stdout(apr_file_t **thefile, apr_pool_t *pool) 740{ 741 return apr_file_open_flags_stdout(thefile, 0, pool); 742} 743 744APR_DECLARE(apr_status_t) apr_file_open_stdin(apr_file_t **thefile, apr_pool_t *pool) 745{ 746 return apr_file_open_flags_stdin(thefile, 0, pool); 747} 748 749APR_POOL_IMPLEMENT_ACCESSOR(file); 750 751APR_IMPLEMENT_INHERIT_SET(file, flags, pool, file_cleanup) 752 753APR_IMPLEMENT_INHERIT_UNSET(file, flags, pool, file_cleanup) 754