~ubuntu-branches/ubuntu/feisty/apache2/feisty

« back to all changes in this revision

Viewing changes to srclib/apr/file_io/win32/filestat.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Barth
  • Date: 2006-12-09 21:05:45 UTC
  • mfrom: (0.6.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061209210545-h70s0xaqc2v8vqr2
Tags: 2.2.3-3.2
* Non-maintainer upload.
* 043_ajp_connection_reuse: Patch from upstream Bugzilla, fixing a critical
  issue with regard to connection reuse in mod_proxy_ajp.
  Closes: #396265

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright 2000-2005 The Apache Software Foundation or its licensors, as
 
2
 * applicable.
 
3
 *
 
4
 * Licensed under the Apache License, Version 2.0 (the "License");
 
5
 * you may not use this file except in compliance with the License.
 
6
 * 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.h"
 
18
#include <aclapi.h>
 
19
#include "apr_private.h"
 
20
#include "apr_arch_file_io.h"
 
21
#include "apr_file_io.h"
 
22
#include "apr_general.h"
 
23
#include "apr_strings.h"
 
24
#include "apr_errno.h"
 
25
#include "apr_time.h"
 
26
#include <sys/stat.h>
 
27
#include "apr_arch_atime.h"
 
28
#include "apr_arch_misc.h"
 
29
 
 
30
/* We have to assure that the file name contains no '*'s, or other
 
31
 * wildcards when using FindFirstFile to recover the true file name.
 
32
 */
 
33
static apr_status_t test_safe_name(const char *name)
 
34
{
 
35
    /* Only accept ':' in the second position of the filename,
 
36
     * as the drive letter delimiter:
 
37
     */
 
38
    if (apr_isalpha(*name) && (name[1] == ':')) {
 
39
        name += 2;
 
40
    }
 
41
    while (*name) {
 
42
        if (!IS_FNCHAR(*name) && (*name != '\\') && (*name != '/')) {
 
43
            if (*name == '?' || *name == '*')
 
44
                return APR_EPATHWILD;
 
45
            else
 
46
                return APR_EBADPATH;
 
47
        }
 
48
        ++name;
 
49
    }
 
50
    return APR_SUCCESS;
 
51
}
 
52
 
 
53
static apr_status_t free_localheap(void *heap) {
 
54
    LocalFree(heap);
 
55
    return APR_SUCCESS;
 
56
}
 
57
 
 
58
static apr_gid_t worldid = NULL;
 
59
 
 
60
static void free_world(void)
 
61
{
 
62
    if (worldid) {
 
63
        FreeSid(worldid);
 
64
        worldid = NULL;
 
65
    }
 
66
}
 
67
 
 
68
/* Left bit shifts from World scope to given scope */
 
69
typedef enum prot_scope_e {
 
70
    prot_scope_world = 0,
 
71
    prot_scope_group = 4,
 
72
    prot_scope_user =  8
 
73
} prot_scope_e;
 
74
 
 
75
static apr_fileperms_t convert_prot(ACCESS_MASK acc, prot_scope_e scope)
 
76
{
 
77
    /* These choices are based on the single filesystem bit that controls
 
78
     * the given behavior.  They are -not- recommended for any set protection
 
79
     * function, such a function should -set- use GENERIC_READ/WRITE/EXECUTE
 
80
     */
 
81
    apr_fileperms_t prot = 0;
 
82
    if (acc & FILE_EXECUTE)
 
83
        prot |= APR_WEXECUTE;
 
84
    if (acc & FILE_WRITE_DATA)
 
85
        prot |= APR_WWRITE;
 
86
    if (acc & FILE_READ_DATA)
 
87
        prot |= APR_WREAD;
 
88
    return (prot << scope);
 
89
}
 
90
 
 
91
static void resolve_prot(apr_finfo_t *finfo, apr_int32_t wanted, PACL dacl)
 
92
{
 
93
    TRUSTEE_W ident = {NULL, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID};
 
94
    ACCESS_MASK acc;
 
95
    /*
 
96
     * This function is only invoked for WinNT, 
 
97
     * there is no reason for os_level testing here.
 
98
     */
 
99
    if ((wanted & APR_FINFO_WPROT) && !worldid) {
 
100
        SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_WORLD_SID_AUTHORITY;
 
101
        if (AllocateAndInitializeSid(&SIDAuth, 1, SECURITY_WORLD_RID,
 
102
                                     0, 0, 0, 0, 0, 0, 0, &worldid))
 
103
            atexit(free_world);
 
104
        else
 
105
            worldid = NULL;
 
106
    }
 
107
    if ((wanted & APR_FINFO_UPROT) && (finfo->valid & APR_FINFO_USER)) {
 
108
        ident.TrusteeType = TRUSTEE_IS_USER;
 
109
        ident.ptstrName = finfo->user;
 
110
        /* GetEffectiveRightsFromAcl isn't supported under Win9x,
 
111
         * which shouldn't come as a surprize.  Since we are passing
 
112
         * TRUSTEE_IS_SID, always skip the A->W layer.
 
113
         */
 
114
        if (GetEffectiveRightsFromAclW(dacl, &ident, &acc) == ERROR_SUCCESS) {
 
115
            finfo->protection |= convert_prot(acc, prot_scope_user);
 
116
            finfo->valid |= APR_FINFO_UPROT;
 
117
        }
 
118
    }
 
119
    /* Windows NT: did not return group rights.
 
120
     * Windows 2000 returns group rights information.
 
121
     * Since WinNT kernels don't follow the unix model of 
 
122
     * group associations, this all all pretty mute.
 
123
     */
 
124
    if ((wanted & APR_FINFO_GPROT) && (finfo->valid & APR_FINFO_GROUP)) {
 
125
        ident.TrusteeType = TRUSTEE_IS_GROUP;
 
126
        ident.ptstrName = finfo->group;
 
127
        if (GetEffectiveRightsFromAclW(dacl, &ident, &acc) == ERROR_SUCCESS) {
 
128
            finfo->protection |= convert_prot(acc, prot_scope_group);
 
129
            finfo->valid |= APR_FINFO_GPROT;
 
130
        }
 
131
    }
 
132
    if ((wanted & APR_FINFO_WPROT) && (worldid)) {
 
133
        ident.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
 
134
        ident.ptstrName = worldid;
 
135
        if (GetEffectiveRightsFromAclW(dacl, &ident, &acc) == ERROR_SUCCESS) {
 
136
            finfo->protection |= convert_prot(acc, prot_scope_world);
 
137
            finfo->valid |= APR_FINFO_WPROT;
 
138
        }
 
139
    }
 
140
}
 
141
 
 
142
static apr_status_t resolve_ident(apr_finfo_t *finfo, const char *fname,
 
143
                                  apr_int32_t wanted, apr_pool_t *pool)
 
144
{
 
145
    apr_file_t *thefile = NULL;
 
146
    apr_status_t rv;
 
147
    /* 
 
148
     * NT5 (W2K) only supports symlinks in the same manner as mount points.
 
149
     * This code should eventually take that into account, for now treat
 
150
     * every reparse point as a symlink...
 
151
     *
 
152
     * We must open the file with READ_CONTROL if we plan to retrieve the
 
153
     * user, group or permissions.
 
154
     */
 
155
    
 
156
    if ((rv = apr_file_open(&thefile, fname, APR_OPENINFO
 
157
                          | ((wanted & APR_FINFO_LINK) ? APR_OPENLINK : 0)
 
158
                          | ((wanted & (APR_FINFO_PROT | APR_FINFO_OWNER))
 
159
                                ? APR_READCONTROL : 0),
 
160
                            APR_OS_DEFAULT, pool)) == APR_SUCCESS) {
 
161
        rv = apr_file_info_get(finfo, wanted, thefile);
 
162
        finfo->filehand = NULL;
 
163
        apr_file_close(thefile);
 
164
    }
 
165
    else if (APR_STATUS_IS_EACCES(rv) && (wanted & (APR_FINFO_PROT 
 
166
                                                  | APR_FINFO_OWNER))) {
 
167
        /* We have a backup plan.  Perhaps we couldn't grab READ_CONTROL?
 
168
         * proceed without asking for that permission...
 
169
         */
 
170
        if ((rv = apr_file_open(&thefile, fname, APR_OPENINFO
 
171
                              | ((wanted & APR_FINFO_LINK) ? APR_OPENLINK : 0),
 
172
                                APR_OS_DEFAULT, pool)) == APR_SUCCESS) {
 
173
            rv = apr_file_info_get(finfo, wanted & ~(APR_FINFO_PROT 
 
174
                                                 | APR_FINFO_OWNER),
 
175
                                 thefile);
 
176
            finfo->filehand = NULL;
 
177
            apr_file_close(thefile);
 
178
        }
 
179
    }
 
180
 
 
181
    if (rv != APR_SUCCESS && rv != APR_INCOMPLETE)
 
182
        return (rv);
 
183
 
 
184
    /* We picked up this case above and had opened the link's properties */
 
185
    if (wanted & APR_FINFO_LINK)
 
186
        finfo->valid |= APR_FINFO_LINK;
 
187
 
 
188
    return rv;
 
189
}
 
190
 
 
191
static void guess_protection_bits(apr_finfo_t *finfo)
 
192
{
 
193
    /* Read, write execute for owner.  In the Win9x environment, any
 
194
     * readable file is executable (well, not entirely 100% true, but
 
195
     * still looking for some cheap logic that would help us here.)
 
196
     * The same holds on NT if a file doesn't have a DACL (e.g., on FAT)
 
197
     */
 
198
    if (finfo->protection & APR_FREADONLY) {
 
199
        finfo->protection |= APR_WREAD | APR_WEXECUTE;
 
200
    }
 
201
    else {
 
202
        finfo->protection |= APR_WREAD | APR_WEXECUTE | APR_WWRITE;
 
203
    }
 
204
    finfo->protection |= (finfo->protection << prot_scope_group)
 
205
                       | (finfo->protection << prot_scope_user);
 
206
 
 
207
    finfo->valid |= APR_FINFO_UPROT | APR_FINFO_GPROT | APR_FINFO_WPROT;
 
208
}
 
209
 
 
210
apr_status_t more_finfo(apr_finfo_t *finfo, const void *ufile, 
 
211
                        apr_int32_t wanted, int whatfile)
 
212
{
 
213
    PSID user = NULL, grp = NULL;
 
214
    PACL dacl = NULL;
 
215
    apr_status_t rv;
 
216
 
 
217
    if (apr_os_level < APR_WIN_NT)
 
218
        guess_protection_bits(finfo);
 
219
    else if (wanted & (APR_FINFO_PROT | APR_FINFO_OWNER))
 
220
    {
 
221
        /* On NT this request is incredibly expensive, but accurate.
 
222
         * Since the WinNT-only functions below are protected by the
 
223
         * (apr_os_level < APR_WIN_NT) case above, we need no extra
 
224
         * tests, but remember GetNamedSecurityInfo & GetSecurityInfo
 
225
         * are not supported on 9x.
 
226
         */
 
227
        SECURITY_INFORMATION sinf = 0;
 
228
        PSECURITY_DESCRIPTOR pdesc = NULL;
 
229
        if (wanted & (APR_FINFO_USER | APR_FINFO_UPROT))
 
230
            sinf |= OWNER_SECURITY_INFORMATION;
 
231
        if (wanted & (APR_FINFO_GROUP | APR_FINFO_GPROT))
 
232
            sinf |= GROUP_SECURITY_INFORMATION;
 
233
        if (wanted & APR_FINFO_PROT)
 
234
            sinf |= DACL_SECURITY_INFORMATION;
 
235
        if (whatfile == MORE_OF_WFSPEC) {
 
236
            apr_wchar_t *wfile = (apr_wchar_t*) ufile;
 
237
            int fix = 0;
 
238
            if (wcsncmp(wfile, L"\\\\?\\", 4) == 0) {
 
239
                fix = 4;
 
240
                if (wcsncmp(wfile + fix, L"UNC\\", 4) == 0)
 
241
                    wfile[6] = L'\\', fix = 6;
 
242
            }
 
243
            rv = GetNamedSecurityInfoW(wfile + fix, 
 
244
                                 SE_FILE_OBJECT, sinf,
 
245
                                 ((wanted & APR_FINFO_USER) ? &user : NULL),
 
246
                                 ((wanted & APR_FINFO_GROUP) ? &grp : NULL),
 
247
                                 ((wanted & APR_FINFO_PROT) ? &dacl : NULL),
 
248
                                 NULL, &pdesc);
 
249
            if (fix == 6)
 
250
                wfile[6] = L'C';
 
251
        }
 
252
        else if (whatfile == MORE_OF_FSPEC)
 
253
            rv = GetNamedSecurityInfoA((char*)ufile, 
 
254
                                 SE_FILE_OBJECT, sinf,
 
255
                                 ((wanted & APR_FINFO_USER) ? &user : NULL),
 
256
                                 ((wanted & APR_FINFO_GROUP) ? &grp : NULL),
 
257
                                 ((wanted & APR_FINFO_PROT) ? &dacl : NULL),
 
258
                                 NULL, &pdesc);
 
259
        else if (whatfile == MORE_OF_HANDLE)
 
260
            rv = GetSecurityInfo((HANDLE)ufile, 
 
261
                                 SE_FILE_OBJECT, sinf,
 
262
                                 ((wanted & APR_FINFO_USER) ? &user : NULL),
 
263
                                 ((wanted & APR_FINFO_GROUP) ? &grp : NULL),
 
264
                                 ((wanted & APR_FINFO_PROT) ? &dacl : NULL),
 
265
                                 NULL, &pdesc);
 
266
        else
 
267
            return APR_INCOMPLETE;
 
268
        if (rv == ERROR_SUCCESS)
 
269
            apr_pool_cleanup_register(finfo->pool, pdesc, free_localheap, 
 
270
                                 apr_pool_cleanup_null);
 
271
        else
 
272
            user = grp = dacl = NULL;
 
273
 
 
274
        if (user) {
 
275
            finfo->user = user;
 
276
            finfo->valid |= APR_FINFO_USER;
 
277
        }
 
278
 
 
279
        if (grp) {
 
280
            finfo->group = grp;
 
281
            finfo->valid |= APR_FINFO_GROUP;
 
282
        }
 
283
 
 
284
        if (dacl) {
 
285
            /* Retrieved the discresionary access list */
 
286
            resolve_prot(finfo, wanted, dacl);
 
287
        }
 
288
        else if (wanted & APR_FINFO_PROT)
 
289
            guess_protection_bits(finfo);
 
290
    }
 
291
 
 
292
    return ((wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS);
 
293
}
 
294
 
 
295
 
 
296
/* This generic fillin depends upon byhandle to be passed as 0 when
 
297
 * a WIN32_FILE_ATTRIBUTE_DATA or either WIN32_FIND_DATA [A or W] is
 
298
 * passed for wininfo.  When the BY_HANDLE_FILE_INFORMATION structure
 
299
 * is passed for wininfo, byhandle is passed as 1 to offset the one
 
300
 * dword discrepancy in offset of the High/Low size structure members.
 
301
 *
 
302
 * The generic fillin returns 1 if the caller should further inquire
 
303
 * if this is a CHR filetype.  If it's reasonably certain it can't be,
 
304
 * then the function returns 0.
 
305
 */
 
306
int fillin_fileinfo(apr_finfo_t *finfo, 
 
307
                    WIN32_FILE_ATTRIBUTE_DATA *wininfo, 
 
308
                    int byhandle, apr_int32_t wanted) 
 
309
{
 
310
    DWORD *sizes = &wininfo->nFileSizeHigh + byhandle;
 
311
    int warn = 0;
 
312
 
 
313
    memset(finfo, '\0', sizeof(*finfo));
 
314
 
 
315
    FileTimeToAprTime(&finfo->atime, &wininfo->ftLastAccessTime);
 
316
    FileTimeToAprTime(&finfo->ctime, &wininfo->ftCreationTime);
 
317
    FileTimeToAprTime(&finfo->mtime, &wininfo->ftLastWriteTime);
 
318
 
 
319
#if APR_HAS_LARGE_FILES
 
320
    finfo->size =  (apr_off_t)sizes[1]
 
321
                | ((apr_off_t)sizes[0] << 32);
 
322
#else
 
323
    finfo->size = (apr_off_t)sizes[1];
 
324
    if (finfo->size < 0 || sizes[0])
 
325
        finfo->size = 0x7fffffff;
 
326
#endif
 
327
 
 
328
    if (wanted & APR_FINFO_LINK &&
 
329
        wininfo->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
 
330
        finfo->filetype = APR_LNK;
 
331
    }
 
332
    else if (wininfo->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
 
333
        finfo->filetype = APR_DIR;
 
334
    }
 
335
    else if (wininfo->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) {
 
336
        /* Warning: This test only succeeds on Win9x, on NT these files
 
337
         * (con, aux, nul, lpt#, com# etc) escape early detection!
 
338
         */
 
339
        finfo->filetype = APR_CHR;
 
340
    }
 
341
    else {
 
342
        /* Warning: Short of opening the handle to the file, the 'FileType'
 
343
         * appears to be unknowable (in any trustworthy or consistent sense)
 
344
         * on WinNT/2K as far as PIPE, CHR, etc are concerned.
 
345
         */
 
346
        if (!wininfo->ftLastWriteTime.dwLowDateTime 
 
347
                && !wininfo->ftLastWriteTime.dwHighDateTime 
 
348
                && !finfo->size)
 
349
            warn = 1;
 
350
        finfo->filetype = APR_REG;
 
351
    }
 
352
 
 
353
    /* The following flags are [for this moment] private to Win32.
 
354
     * That's the only excuse for not toggling valid bits to reflect them.
 
355
     */
 
356
    if (wininfo->dwFileAttributes & FILE_ATTRIBUTE_READONLY)
 
357
        finfo->protection = APR_FREADONLY;
 
358
    
 
359
    finfo->valid = APR_FINFO_ATIME | APR_FINFO_CTIME | APR_FINFO_MTIME
 
360
                 | APR_FINFO_SIZE  | APR_FINFO_TYPE;   /* == APR_FINFO_MIN */
 
361
 
 
362
    /* Only byhandle optionally tests link targets, so tell that caller
 
363
     * what it wants to hear, otherwise the byattributes is never
 
364
     * reporting anything but the link.
 
365
     */
 
366
    if (!byhandle || (wanted & APR_FINFO_LINK))
 
367
        finfo->valid |= APR_FINFO_LINK;
 
368
    return warn;
 
369
}
 
370
 
 
371
 
 
372
APR_DECLARE(apr_status_t) apr_file_info_get(apr_finfo_t *finfo, apr_int32_t wanted,
 
373
                                            apr_file_t *thefile)
 
374
{
 
375
    BY_HANDLE_FILE_INFORMATION FileInfo;
 
376
 
 
377
    if (thefile->buffered) {
 
378
        /* XXX: flush here is not mutex protected */
 
379
        apr_status_t rv = apr_file_flush(thefile);
 
380
        if (rv != APR_SUCCESS)
 
381
            return rv;
 
382
    }
 
383
 
 
384
    if (!GetFileInformationByHandle(thefile->filehand, &FileInfo)) {
 
385
        return apr_get_os_error();
 
386
    }
 
387
 
 
388
    fillin_fileinfo(finfo, (WIN32_FILE_ATTRIBUTE_DATA *) &FileInfo, 1, wanted);
 
389
 
 
390
    if (finfo->filetype == APR_REG)
 
391
    {
 
392
        /* Go the extra mile to be -certain- that we have a real, regular
 
393
         * file, since the attribute bits aren't a certain thing.  Even
 
394
         * though fillin should have hinted if we *must* do this, we
 
395
         * don't need to take chances while the handle is already open.
 
396
         */
 
397
        DWORD FileType;
 
398
        if (FileType = GetFileType(thefile->filehand)) {
 
399
            if (FileType == FILE_TYPE_CHAR) {
 
400
                finfo->filetype = APR_CHR;
 
401
            }
 
402
            else if (FileType == FILE_TYPE_PIPE) {
 
403
                finfo->filetype = APR_PIPE;
 
404
            }
 
405
            /* Otherwise leave the original conclusion alone 
 
406
             */
 
407
        }
 
408
    }
 
409
 
 
410
    finfo->pool = thefile->pool;
 
411
 
 
412
    /* ### The finfo lifetime may exceed the lifetime of thefile->pool
 
413
     * but finfo's aren't managed in pools, so where on earth would
 
414
     * we pstrdup the fname into???
 
415
     */
 
416
    finfo->fname = thefile->fname;
 
417
 
 
418
    /* Extra goodies known only by GetFileInformationByHandle() */
 
419
    finfo->inode  =  (apr_ino_t)FileInfo.nFileIndexLow
 
420
                  | ((apr_ino_t)FileInfo.nFileIndexHigh << 32);
 
421
    finfo->device = FileInfo.dwVolumeSerialNumber;
 
422
    finfo->nlink  = FileInfo.nNumberOfLinks;
 
423
 
 
424
    finfo->valid |= APR_FINFO_IDENT | APR_FINFO_NLINK;
 
425
 
 
426
    /* If we still want something more (besides the name) go get it! 
 
427
     */
 
428
    if ((wanted &= ~finfo->valid) & ~APR_FINFO_NAME) {
 
429
        return more_finfo(finfo, thefile->filehand, wanted, MORE_OF_HANDLE);
 
430
    }
 
431
 
 
432
    return APR_SUCCESS;
 
433
}
 
434
 
 
435
APR_DECLARE(apr_status_t) apr_file_perms_set(const char *fname,
 
436
                                           apr_fileperms_t perms)
 
437
{
 
438
    return APR_ENOTIMPL;
 
439
}
 
440
 
 
441
APR_DECLARE(apr_status_t) apr_stat(apr_finfo_t *finfo, const char *fname,
 
442
                                   apr_int32_t wanted, apr_pool_t *pool)
 
443
{
 
444
    /* XXX: is constant - needs testing - which requires a lighter-weight root test fn */
 
445
    int isroot = 0;
 
446
    apr_status_t ident_rv = 0;
 
447
    apr_status_t rv;
 
448
#if APR_HAS_UNICODE_FS
 
449
    apr_wchar_t wfname[APR_PATH_MAX];
 
450
 
 
451
#endif
 
452
    char *filename = NULL;
 
453
    /* These all share a common subset of this structure */
 
454
    union {
 
455
        WIN32_FIND_DATAW w;
 
456
        WIN32_FIND_DATAA n;
 
457
        WIN32_FILE_ATTRIBUTE_DATA i;
 
458
    } FileInfo;
 
459
    
 
460
    /* Catch fname length == MAX_PATH since GetFileAttributesEx fails 
 
461
     * with PATH_NOT_FOUND.  We would rather indicate length error than 
 
462
     * 'not found'
 
463
     */        
 
464
    if (strlen(fname) >= APR_PATH_MAX) {
 
465
        return APR_ENAMETOOLONG;
 
466
    }
 
467
 
 
468
#if APR_HAS_UNICODE_FS
 
469
    IF_WIN_OS_IS_UNICODE
 
470
    {
 
471
        if ((wanted & (APR_FINFO_IDENT | APR_FINFO_NLINK)) 
 
472
               || (~wanted & APR_FINFO_LINK)) {
 
473
            /* FindFirstFile and GetFileAttributesEx can't figure the inode,
 
474
             * device or number of links, so we need to resolve with an open 
 
475
             * file handle.  If the user has asked for these fields, fall over 
 
476
             * to the get file info by handle method.  If we fail, or the user
 
477
             * also asks for the file name, continue by our usual means.
 
478
             *
 
479
             * We also must use this method for a 'true' stat, that resolves
 
480
             * a symlink (NTFS Junction) target.  This is because all fileinfo
 
481
             * on a Junction always returns the junction, opening the target
 
482
             * is the only way to resolve the target's attributes.
 
483
             */
 
484
            if ((ident_rv = resolve_ident(finfo, fname, wanted, pool)) 
 
485
                    == APR_SUCCESS)
 
486
                return ident_rv;
 
487
            else if (ident_rv == APR_INCOMPLETE)
 
488
                wanted &= ~finfo->valid;
 
489
        }
 
490
 
 
491
        if (rv = utf8_to_unicode_path(wfname, sizeof(wfname) 
 
492
                                            / sizeof(apr_wchar_t), fname))
 
493
            return rv;
 
494
        if (!(wanted & APR_FINFO_NAME)) {
 
495
            if (!GetFileAttributesExW(wfname, GetFileExInfoStandard, 
 
496
                                      &FileInfo.i))
 
497
                return apr_get_os_error();
 
498
        }
 
499
        else {
 
500
            /* Guard against bogus wildcards and retrieve by name
 
501
             * since we want the true name, and set aside a long
 
502
             * enough string to handle the longest file name.
 
503
             */
 
504
            char tmpname[APR_FILE_MAX * 3 + 1];
 
505
            HANDLE hFind;
 
506
            if ((rv = test_safe_name(fname)) != APR_SUCCESS) {
 
507
                return rv;
 
508
            }
 
509
            hFind = FindFirstFileW(wfname, &FileInfo.w);
 
510
            if (hFind == INVALID_HANDLE_VALUE)
 
511
                return apr_get_os_error();
 
512
            FindClose(hFind);
 
513
            if (unicode_to_utf8_path(tmpname, sizeof(tmpname), 
 
514
                                     FileInfo.w.cFileName)) {
 
515
                return APR_ENAMETOOLONG;
 
516
            }
 
517
            filename = apr_pstrdup(pool, tmpname);
 
518
        }
 
519
    }
 
520
#endif
 
521
#if APR_HAS_ANSI_FS
 
522
    ELSE_WIN_OS_IS_ANSI
 
523
    {
 
524
        char *root = NULL;
 
525
        const char *test = fname;
 
526
        rv = apr_filepath_root(&root, &test, APR_FILEPATH_NATIVE, pool);
 
527
        isroot = (root && *root && !(*test));
 
528
 
 
529
        if ((apr_os_level >= APR_WIN_98) && (!(wanted & APR_FINFO_NAME) || isroot))
 
530
        {
 
531
            /* cannot use FindFile on a Win98 root, it returns \*
 
532
             * GetFileAttributesExA is not available on Win95
 
533
             */
 
534
            if (!GetFileAttributesExA(fname, GetFileExInfoStandard, 
 
535
                                     &FileInfo.i)) {
 
536
                return apr_get_os_error();
 
537
            }
 
538
        }
 
539
        else if (isroot) {
 
540
            /* This is Win95 and we are trying to stat a root.  Lie.
 
541
             */
 
542
            if (GetDriveType(fname) != DRIVE_UNKNOWN) 
 
543
            {
 
544
                finfo->pool = pool;
 
545
                finfo->filetype = 0;
 
546
                finfo->mtime = apr_time_now();
 
547
                finfo->protection |= APR_WREAD | APR_WEXECUTE | APR_WWRITE;
 
548
                finfo->protection |= (finfo->protection << prot_scope_group) 
 
549
                                   | (finfo->protection << prot_scope_user);
 
550
                finfo->valid |= APR_FINFO_TYPE | APR_FINFO_PROT 
 
551
                              | APR_FINFO_MTIME
 
552
                              | (wanted & APR_FINFO_LINK);
 
553
                return (wanted &= ~finfo->valid) ? APR_INCOMPLETE 
 
554
                                                 : APR_SUCCESS;
 
555
            }
 
556
            else
 
557
                return APR_FROM_OS_ERROR(ERROR_PATH_NOT_FOUND);
 
558
        }
 
559
        else  {
 
560
            /* Guard against bogus wildcards and retrieve by name
 
561
             * since we want the true name, or are stuck in Win95,
 
562
             * or are looking for the root of a Win98 drive.
 
563
             */
 
564
            HANDLE hFind;
 
565
            if ((rv = test_safe_name(fname)) != APR_SUCCESS) {
 
566
                return rv;
 
567
            }
 
568
            hFind = FindFirstFileA(fname, &FileInfo.n);
 
569
            if (hFind == INVALID_HANDLE_VALUE) {
 
570
                return apr_get_os_error();
 
571
            } 
 
572
            FindClose(hFind);
 
573
            filename = apr_pstrdup(pool, FileInfo.n.cFileName);
 
574
        }
 
575
    }
 
576
#endif
 
577
 
 
578
    if (ident_rv != APR_INCOMPLETE) {
 
579
        if (fillin_fileinfo(finfo, (WIN32_FILE_ATTRIBUTE_DATA *) &FileInfo, 
 
580
                            0, wanted))
 
581
        {
 
582
            /* Go the extra mile to assure we have a file.  WinNT/2000 seems
 
583
             * to reliably translate char devices to the path '\\.\device'
 
584
             * so go ask for the full path.
 
585
             */
 
586
            if (apr_os_level >= APR_WIN_NT)
 
587
            {
 
588
#if APR_HAS_UNICODE_FS
 
589
                apr_wchar_t tmpname[APR_FILE_MAX];
 
590
                apr_wchar_t *tmpoff = NULL;
 
591
                if (GetFullPathNameW(wfname, sizeof(tmpname) / sizeof(apr_wchar_t),
 
592
                                     tmpname, &tmpoff))
 
593
                {
 
594
                    if (!wcsncmp(tmpname, L"\\\\.\\", 4)) {
 
595
#else
 
596
                /* Same initial logic as above, but
 
597
                 * only for WinNT/non-UTF-8 builds of APR:
 
598
                 */
 
599
                char tmpname[APR_FILE_MAX];
 
600
                char *tmpoff;
 
601
                if (GetFullPathName(fname, sizeof(tmpname), tmpname, &tmpoff))
 
602
                {
 
603
                    if (!strncmp(tmpname, "\\\\.\\", 4)) {
 
604
#endif
 
605
                        if (tmpoff == tmpname + 4) {
 
606
                            finfo->filetype = APR_CHR;
 
607
                        }
 
608
                        /* For WHATEVER reason, CHR devices such as \\.\con 
 
609
                         * or \\.\lpt1 *may*not* update tmpoff; in fact the
 
610
                         * resulting tmpoff is set to NULL.  Guard against 
 
611
                         * either case.
 
612
                         *
 
613
                         * This code is identical for wide and narrow chars...
 
614
                         */
 
615
                        else if (!tmpoff) {
 
616
                            tmpoff = tmpname + 4;
 
617
                            while (*tmpoff) {
 
618
                                if (*tmpoff == '\\' || *tmpoff == '/') {
 
619
                                    break;
 
620
                                }
 
621
                                ++tmpoff;
 
622
                            }
 
623
                            if (!*tmpoff) {
 
624
                                finfo->filetype = APR_CHR;
 
625
                            }
 
626
                        }
 
627
                    }
 
628
                }
 
629
                else {
 
630
                    finfo->valid &= ~APR_FINFO_TYPE;
 
631
                }
 
632
 
 
633
            }
 
634
            else {
 
635
                finfo->valid &= ~APR_FINFO_TYPE;
 
636
            }
 
637
        }
 
638
        finfo->pool = pool;
 
639
    }
 
640
 
 
641
    if (filename && !isroot) {
 
642
        finfo->name = filename;
 
643
        finfo->valid |= APR_FINFO_NAME;
 
644
    }
 
645
 
 
646
    if (wanted &= ~finfo->valid) {
 
647
        /* Caller wants more than APR_FINFO_MIN | APR_FINFO_NAME */
 
648
#if APR_HAS_UNICODE_FS
 
649
        if (apr_os_level >= APR_WIN_NT)
 
650
            return more_finfo(finfo, wfname, wanted, MORE_OF_WFSPEC);
 
651
#endif
 
652
        return more_finfo(finfo, fname, wanted, MORE_OF_FSPEC);
 
653
    }
 
654
 
 
655
    return APR_SUCCESS;
 
656
}
 
657
 
 
658
APR_DECLARE(apr_status_t) apr_file_attrs_set(const char *fname,
 
659
                                             apr_fileattrs_t attributes,
 
660
                                             apr_fileattrs_t attr_mask,
 
661
                                             apr_pool_t *pool)
 
662
{
 
663
    DWORD flags;
 
664
    apr_status_t rv;
 
665
#if APR_HAS_UNICODE_FS
 
666
    apr_wchar_t wfname[APR_PATH_MAX];
 
667
#endif
 
668
 
 
669
    /* Don't do anything if we can't handle the requested attributes */
 
670
    if (!(attr_mask & (APR_FILE_ATTR_READONLY
 
671
                       | APR_FILE_ATTR_HIDDEN)))
 
672
        return APR_SUCCESS;
 
673
 
 
674
#if APR_HAS_UNICODE_FS
 
675
    IF_WIN_OS_IS_UNICODE
 
676
    {
 
677
        if (rv = utf8_to_unicode_path(wfname,
 
678
                                      sizeof(wfname) / sizeof(wfname[0]),
 
679
                                      fname))
 
680
            return rv;
 
681
        flags = GetFileAttributesW(wfname);
 
682
    }
 
683
#endif
 
684
#if APR_HAS_ANSI_FS
 
685
    ELSE_WIN_OS_IS_ANSI
 
686
    {
 
687
        flags = GetFileAttributesA(fname);
 
688
    }
 
689
#endif
 
690
 
 
691
    if (flags == 0xFFFFFFFF)
 
692
        return apr_get_os_error();
 
693
 
 
694
    if (attr_mask & APR_FILE_ATTR_READONLY)
 
695
    {
 
696
        if (attributes & APR_FILE_ATTR_READONLY)
 
697
            flags |= FILE_ATTRIBUTE_READONLY;
 
698
        else
 
699
            flags &= ~FILE_ATTRIBUTE_READONLY;
 
700
    }
 
701
 
 
702
    if (attr_mask & APR_FILE_ATTR_HIDDEN)
 
703
    {
 
704
        if (attributes & APR_FILE_ATTR_HIDDEN)
 
705
            flags |= FILE_ATTRIBUTE_HIDDEN;
 
706
        else
 
707
            flags &= ~FILE_ATTRIBUTE_HIDDEN;
 
708
    }
 
709
 
 
710
#if APR_HAS_UNICODE_FS
 
711
    IF_WIN_OS_IS_UNICODE
 
712
    {
 
713
        rv = SetFileAttributesW(wfname, flags);
 
714
    }
 
715
#endif
 
716
#if APR_HAS_ANSI_FS
 
717
    ELSE_WIN_OS_IS_ANSI
 
718
    {
 
719
        rv = SetFileAttributesA(fname, flags);
 
720
    }
 
721
#endif
 
722
 
 
723
    if (rv == 0)
 
724
        return apr_get_os_error();
 
725
 
 
726
    return APR_SUCCESS;
 
727
}
 
728
 
 
729
 
 
730
APR_DECLARE(apr_status_t) apr_file_mtime_set(const char *fname,
 
731
                                             apr_time_t mtime,
 
732
                                             apr_pool_t *pool)
 
733
{
 
734
    apr_file_t *thefile;
 
735
    apr_status_t rv;
 
736
 
 
737
    rv = apr_file_open(&thefile, fname,
 
738
                       APR_READ | APR_WRITEATTRS,
 
739
                       APR_OS_DEFAULT, pool);
 
740
    if (!rv)
 
741
    {
 
742
        FILETIME file_ctime;
 
743
        FILETIME file_atime;
 
744
        FILETIME file_mtime;
 
745
 
 
746
        if (!GetFileTime(thefile->filehand,
 
747
                         &file_ctime, &file_atime, &file_mtime))
 
748
            rv = apr_get_os_error();
 
749
        else
 
750
        {
 
751
            AprTimeToFileTime(&file_mtime, mtime);
 
752
            if (!SetFileTime(thefile->filehand,
 
753
                             &file_ctime, &file_atime, &file_mtime))
 
754
                rv = apr_get_os_error();
 
755
        }
 
756
 
 
757
        apr_file_close(thefile);
 
758
    }
 
759
 
 
760
    return rv;
 
761
}