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 |
#ifdef 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 |
38 |
apr_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 |
* 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. |
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 |
|
101 |
apr_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 |
|
140 |
void *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 |
226 |
static 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 |
|
277 |
apr_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 |
|
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, |
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_NONBLOCK) { |
333 |
return APR_ENOTIMPL; |
334 |
} |
335 |
if (flag & APR_FOPEN_READ) { |
336 |
oflags |= GENERIC_READ; |
337 |
} |
338 |
if (flag & APR_FOPEN_WRITE) { |
339 |
oflags |= GENERIC_WRITE; |
340 |
} |
341 |
if (flag & APR_WRITEATTRS) { |
342 |
oflags |= FILE_WRITE_ATTRIBUTES; |
343 |
} |
344 |
|
345 |
if (apr_os_level >= APR_WIN_NT) |
346 |
sharemode |= FILE_SHARE_DELETE; |
347 |
|
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; |
355 |
} else { |
356 |
/* open existing but create if necessary */ |
357 |
createflags = OPEN_ALWAYS; |
358 |
} |
359 |
} else if (flag & APR_FOPEN_TRUNCATE) { |
360 |
/* only truncate if file already exists */ |
361 |
createflags = TRUNCATE_EXISTING; |
362 |
} else { |
363 |
/* only open if file already exists */ |
364 |
createflags = OPEN_EXISTING; |
365 |
} |
366 |
|
367 |
if ((flag & APR_FOPEN_EXCL) && !(flag & APR_FOPEN_CREATE)) { |
368 |
return APR_EACCES; |
369 |
} |
370 |
|
371 |
if (flag & APR_FOPEN_DELONCLOSE) { |
372 |
attributes |= FILE_FLAG_DELETE_ON_CLOSE; |
373 |
} |
374 |
|
375 |
if (flag & APR_OPENLINK) { |
376 |
attributes |= FILE_FLAG_OPEN_REPARSE_POINT; |
377 |
} |
378 |
|
379 |
/* Without READ or WRITE, we fail unless apr called apr_file_open |
380 |
* internally with the private APR_OPENINFO flag. |
381 |
* |
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 |
385 |
*/ |
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; |
390 |
} |
391 |
} |
392 |
else { |
393 |
return APR_EACCES; |
394 |
} |
395 |
if (flag & APR_READCONTROL) |
396 |
oflags |= READ_CONTROL; |
397 |
} |
398 |
|
399 |
if (flag & APR_FOPEN_XTHREAD) { |
400 |
/* This win32 specific feature is required |
401 |
* to allow multiple threads to work with the file. |
402 |
*/ |
403 |
attributes |= FILE_FLAG_OVERLAPPED; |
404 |
} |
405 |
|
406 |
#if APR_HAS_UNICODE_FS |
407 |
IF_WIN_OS_IS_UNICODE |
408 |
{ |
409 |
apr_wchar_t wfname[APR_PATH_MAX]; |
410 |
|
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. |
414 |
*/ |
415 |
flag |= APR_FOPEN_XTHREAD; |
416 |
attributes |= FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED; |
417 |
} |
418 |
|
419 |
if ((rv = utf8_to_unicode_path(wfname, sizeof(wfname) |
420 |
/ sizeof(apr_wchar_t), fname))) |
421 |
return rv; |
422 |
handle = CreateFileW(wfname, oflags, sharemode, |
423 |
NULL, createflags, attributes, 0); |
424 |
} |
425 |
#endif |
426 |
#if APR_HAS_ANSI_FS |
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; |
432 |
} |
433 |
#endif |
434 |
if (handle == INVALID_HANDLE_VALUE) { |
435 |
return apr_get_os_error(); |
436 |
} |
437 |
|
438 |
(*new) = (apr_file_t *)apr_pcalloc(pool, sizeof(apr_file_t)); |
439 |
(*new)->pool = pool; |
440 |
(*new)->filehand = handle; |
441 |
(*new)->fname = apr_pstrdup(pool, fname); |
442 |
(*new)->flags = flag; |
443 |
(*new)->timeout = -1; |
444 |
(*new)->ungetchar = -1; |
445 |
|
446 |
if (flag & APR_FOPEN_APPEND) { |
447 |
(*new)->append = 1; |
448 |
SetFilePointer((*new)->filehand, 0, NULL, FILE_END); |
449 |
} |
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; |
454 |
} |
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); |
459 |
if (rv) { |
460 |
if (file_cleanup(*new) == APR_SUCCESS) { |
461 |
apr_pool_cleanup_kill(pool, *new, file_cleanup); |
462 |
} |
463 |
return rv; |
464 |
} |
465 |
} |
466 |
|
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. |
473 |
*/ |
474 |
(*new)->flags &= ~APR_FOPEN_SPARSE; |
475 |
} |
476 |
else |
477 |
#endif |
478 |
/* This feature is not supported on this platform. */ |
479 |
(*new)->flags &= ~APR_FOPEN_SPARSE; |
480 |
|
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); |
485 |
#endif |
486 |
if (!(flag & APR_FOPEN_NOCLEANUP)) { |
487 |
apr_pool_cleanup_register((*new)->pool, (void *)(*new), file_cleanup, |
488 |
apr_pool_cleanup_null); |
489 |
} |
490 |
return APR_SUCCESS; |
491 |
} |
492 |
|
493 |
APR_DECLARE(apr_status_t) apr_file_close(apr_file_t *file) |
494 |
{ |
495 |
apr_status_t stat; |
496 |
if ((stat = file_cleanup(file)) == APR_SUCCESS) { |
497 |
apr_pool_cleanup_kill(file->pool, file, file_cleanup); |
498 |
|
499 |
if (file->mutex) { |
500 |
apr_thread_mutex_destroy(file->mutex); |
501 |
} |
502 |
|
503 |
return APR_SUCCESS; |
504 |
} |
505 |
return stat; |
506 |
} |
507 |
|
508 |
APR_DECLARE(apr_status_t) apr_file_remove(const char *path, apr_pool_t *pool) |
509 |
{ |
510 |
#if APR_HAS_UNICODE_FS |
511 |
IF_WIN_OS_IS_UNICODE |
512 |
{ |
513 |
apr_wchar_t wpath[APR_PATH_MAX]; |
514 |
apr_status_t rv; |
515 |
if ((rv = utf8_to_unicode_path(wpath, sizeof(wpath) |
516 |
/ sizeof(apr_wchar_t), path))) { |
517 |
return rv; |
518 |
} |
519 |
if (DeleteFileW(wpath)) |
520 |
return APR_SUCCESS; |
521 |
} |
522 |
#endif |
523 |
#if APR_HAS_ANSI_FS |
524 |
ELSE_WIN_OS_IS_ANSI |
525 |
if (DeleteFile(path)) |
526 |
return APR_SUCCESS; |
527 |
#endif |
528 |
return apr_get_os_error(); |
529 |
} |
530 |
|
531 |
APR_DECLARE(apr_status_t) apr_file_rename(const char *frompath, |
532 |
const char *topath, |
533 |
apr_pool_t *pool) |
534 |
{ |
535 |
IF_WIN_OS_IS_UNICODE |
536 |
{ |
537 |
#if APR_HAS_UNICODE_FS |
538 |
apr_wchar_t wfrompath[APR_PATH_MAX], wtopath[APR_PATH_MAX]; |
539 |
apr_status_t rv; |
540 |
if ((rv = utf8_to_unicode_path(wfrompath, |
541 |
sizeof(wfrompath) / sizeof(apr_wchar_t), |
542 |
frompath))) { |
543 |
return rv; |
544 |
} |
545 |
if ((rv = utf8_to_unicode_path(wtopath, |
546 |
sizeof(wtopath) / sizeof(apr_wchar_t), |
547 |
topath))) { |
548 |
return rv; |
549 |
} |
550 |
#ifndef _WIN32_WCE |
551 |
if (MoveFileExW(wfrompath, wtopath, MOVEFILE_REPLACE_EXISTING | |
552 |
MOVEFILE_COPY_ALLOWED)) |
553 |
#else |
554 |
if (MoveFileW(wfrompath, wtopath)) |
555 |
#endif |
556 |
return APR_SUCCESS; |
557 |
#else |
558 |
if (MoveFileEx(frompath, topath, MOVEFILE_REPLACE_EXISTING | |
559 |
MOVEFILE_COPY_ALLOWED)) |
560 |
return APR_SUCCESS; |
561 |
#endif |
562 |
} |
563 |
#if APR_HAS_ANSI_FS |
564 |
ELSE_WIN_OS_IS_ANSI |
565 |
{ |
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? |
571 |
*/ |
572 |
HANDLE handle = INVALID_HANDLE_VALUE; |
573 |
|
574 |
if ((handle = CreateFile(topath, GENERIC_WRITE, 0, 0, |
575 |
OPEN_EXISTING, 0, 0 )) != INVALID_HANDLE_VALUE ) |
576 |
{ |
577 |
CloseHandle(handle); |
578 |
if (!DeleteFile(topath)) |
579 |
return apr_get_os_error(); |
580 |
} |
581 |
if (MoveFile(frompath, topath)) |
582 |
return APR_SUCCESS; |
583 |
} |
584 |
#endif |
585 |
return apr_get_os_error(); |
586 |
} |
587 |
|
588 |
APR_DECLARE(apr_status_t) apr_file_link(const char *from_path, |
589 |
const char *to_path) |
590 |
{ |
591 |
apr_status_t rv = APR_SUCCESS; |
592 |
|
593 |
#if APR_HAS_UNICODE_FS |
594 |
IF_WIN_OS_IS_UNICODE |
595 |
{ |
596 |
apr_wchar_t wfrom_path[APR_PATH_MAX]; |
597 |
apr_wchar_t wto_path[APR_PATH_MAX]; |
598 |
|
599 |
if ((rv = utf8_to_unicode_path(wfrom_path, |
600 |
sizeof(wfrom_path) / sizeof(apr_wchar_t), |
601 |
from_path))) |
602 |
return rv; |
603 |
if ((rv = utf8_to_unicode_path(wto_path, |
604 |
sizeof(wto_path) / sizeof(apr_wchar_t), |
605 |
to_path))) |
606 |
return rv; |
607 |
|
608 |
if (!CreateHardLinkW(wto_path, wfrom_path, NULL)) |
609 |
return apr_get_os_error(); |
610 |
} |
611 |
#endif |
612 |
#if APR_HAS_ANSI_FS |
613 |
ELSE_WIN_OS_IS_ANSI { |
614 |
if (!CreateHardLinkA(to_path, from_path, NULL)) |
615 |
return apr_get_os_error(); |
616 |
} |
617 |
#endif |
618 |
return rv; |
619 |
} |
620 |
|
621 |
APR_DECLARE(apr_status_t) apr_os_file_get(apr_os_file_t *thefile, |
622 |
apr_file_t *file) |
623 |
{ |
624 |
*thefile = file->filehand; |
625 |
return APR_SUCCESS; |
626 |
} |
627 |
|
628 |
APR_DECLARE(apr_status_t) apr_os_file_put(apr_file_t **file, |
629 |
apr_os_file_t *thefile, |
630 |
apr_int32_t flags, |
631 |
apr_pool_t *pool) |
632 |
{ |
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; |
639 |
|
640 |
if (flags & APR_FOPEN_APPEND) { |
641 |
(*file)->append = 1; |
642 |
} |
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; |
647 |
} |
648 |
|
649 |
if ((*file)->append || (*file)->buffered) { |
650 |
apr_status_t rv; |
651 |
rv = apr_thread_mutex_create(&(*file)->mutex, |
652 |
APR_THREAD_MUTEX_DEFAULT, pool); |
653 |
if (rv) { |
654 |
return rv; |
655 |
} |
656 |
} |
657 |
|
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); |
662 |
#endif |
663 |
/* Should we be testing if thefile is a handle to |
664 |
* a PIPE and set up the mechanics appropriately? |
665 |
* |
666 |
* (*file)->pipe; |
667 |
*/ |
668 |
return APR_SUCCESS; |
669 |
} |
670 |
|
671 |
APR_DECLARE(apr_status_t) apr_file_eof(apr_file_t *fptr) |
672 |
{ |
673 |
if (fptr->eof_hit == 1) { |
674 |
return APR_EOF; |
675 |
} |
676 |
return APR_SUCCESS; |
677 |
} |
678 |
|
679 |
APR_DECLARE(apr_status_t) apr_file_open_flags_stderr(apr_file_t **thefile, |
680 |
apr_int32_t flags, |
681 |
apr_pool_t *pool) |
682 |
{ |
683 |
#ifdef _WIN32_WCE |
684 |
return APR_ENOTIMPL; |
685 |
#else |
686 |
apr_os_file_t file_handle; |
687 |
|
688 |
apr_set_os_error(APR_SUCCESS); |
689 |
file_handle = GetStdHandle(STD_ERROR_HANDLE); |
690 |
if (!file_handle) |
691 |
file_handle = INVALID_HANDLE_VALUE; |
692 |
|
693 |
return apr_os_file_put(thefile, &file_handle, |
694 |
flags | APR_FOPEN_WRITE | APR_STDERR_FLAG, pool); |
695 |
#endif |
696 |
} |
697 |
|
698 |
APR_DECLARE(apr_status_t) apr_file_open_flags_stdout(apr_file_t **thefile, |
699 |
apr_int32_t flags, |
700 |
apr_pool_t *pool) |
701 |
{ |
702 |
#ifdef _WIN32_WCE |
703 |
return APR_ENOTIMPL; |
704 |
#else |
705 |
apr_os_file_t file_handle; |
706 |
|
707 |
apr_set_os_error(APR_SUCCESS); |
708 |
file_handle = GetStdHandle(STD_OUTPUT_HANDLE); |
709 |
if (!file_handle) |
710 |
file_handle = INVALID_HANDLE_VALUE; |
711 |
|
712 |
return apr_os_file_put(thefile, &file_handle, |
713 |
flags | APR_FOPEN_WRITE | APR_STDOUT_FLAG, pool); |
714 |
#endif |
715 |
} |
716 |
|
717 |
APR_DECLARE(apr_status_t) apr_file_open_flags_stdin(apr_file_t **thefile, |
718 |
apr_int32_t flags, |
719 |
apr_pool_t *pool) |
720 |
{ |
721 |
#ifdef _WIN32_WCE |
722 |
return APR_ENOTIMPL; |
723 |
#else |
724 |
apr_os_file_t file_handle; |
725 |
|
726 |
apr_set_os_error(APR_SUCCESS); |
727 |
file_handle = GetStdHandle(STD_INPUT_HANDLE); |
728 |
if (!file_handle) |
729 |
file_handle = INVALID_HANDLE_VALUE; |
730 |
|
731 |
return apr_os_file_put(thefile, &file_handle, |
732 |
flags | APR_FOPEN_READ | APR_STDIN_FLAG, pool); |
733 |
#endif |
734 |
} |
735 |
|
736 |
APR_DECLARE(apr_status_t) apr_file_open_stderr(apr_file_t **thefile, apr_pool_t *pool) |
737 |
{ |
738 |
return apr_file_open_flags_stderr(thefile, 0, pool); |
739 |
} |
740 |
|
741 |
APR_DECLARE(apr_status_t) apr_file_open_stdout(apr_file_t **thefile, apr_pool_t *pool) |
742 |
{ |
743 |
return apr_file_open_flags_stdout(thefile, 0, pool); |
744 |
} |
745 |
|
746 |
APR_DECLARE(apr_status_t) apr_file_open_stdin(apr_file_t **thefile, apr_pool_t *pool) |
747 |
{ |
748 |
return apr_file_open_flags_stdin(thefile, 0, pool); |
749 |
} |
750 |
|
751 |
APR_POOL_IMPLEMENT_ACCESSOR(file); |
752 |
|
753 |
APR_IMPLEMENT_INHERIT_SET(file, flags, pool, file_cleanup) |
754 |
|
755 |
APR_IMPLEMENT_INHERIT_UNSET(file, flags, pool, file_cleanup) |