123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761 |
- /* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #include "apr.h"
- #include <aclapi.h>
- #include "apr_private.h"
- #include "apr_arch_file_io.h"
- #include "apr_file_io.h"
- #include "apr_general.h"
- #include "apr_strings.h"
- #include "apr_errno.h"
- #include "apr_time.h"
- #include <sys/stat.h>
- #include "apr_arch_atime.h"
- #include "apr_arch_misc.h"
- /* We have to assure that the file name contains no '*'s, or other
- * wildcards when using FindFirstFile to recover the true file name.
- */
- static apr_status_t test_safe_name(const char *name)
- {
- /* Only accept ':' in the second position of the filename,
- * as the drive letter delimiter:
- */
- if (apr_isalpha(*name) && (name[1] == ':')) {
- name += 2;
- }
- while (*name) {
- if (!IS_FNCHAR(*name) && (*name != '\\') && (*name != '/')) {
- if (*name == '?' || *name == '*')
- return APR_EPATHWILD;
- else
- return APR_EBADPATH;
- }
- ++name;
- }
- return APR_SUCCESS;
- }
- static apr_status_t free_localheap(void *heap) {
- LocalFree(heap);
- return APR_SUCCESS;
- }
- static apr_gid_t worldid = NULL;
- static void free_world(void)
- {
- if (worldid) {
- FreeSid(worldid);
- worldid = NULL;
- }
- }
- /* Left bit shifts from World scope to given scope */
- typedef enum prot_scope_e {
- prot_scope_world = 0,
- prot_scope_group = 4,
- prot_scope_user = 8
- } prot_scope_e;
- static apr_fileperms_t convert_prot(ACCESS_MASK acc, prot_scope_e scope)
- {
- /* These choices are based on the single filesystem bit that controls
- * the given behavior. They are -not- recommended for any set protection
- * function, such a function should -set- use GENERIC_READ/WRITE/EXECUTE
- */
- apr_fileperms_t prot = 0;
- if (acc & FILE_EXECUTE)
- prot |= APR_WEXECUTE;
- if (acc & FILE_WRITE_DATA)
- prot |= APR_WWRITE;
- if (acc & FILE_READ_DATA)
- prot |= APR_WREAD;
- return (prot << scope);
- }
- static void resolve_prot(apr_finfo_t *finfo, apr_int32_t wanted, PACL dacl)
- {
- TRUSTEE_W ident = {NULL, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID};
- ACCESS_MASK acc;
- /*
- * This function is only invoked for WinNT,
- * there is no reason for os_level testing here.
- */
- if ((wanted & APR_FINFO_WPROT) && !worldid) {
- SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_WORLD_SID_AUTHORITY;
- if (AllocateAndInitializeSid(&SIDAuth, 1, SECURITY_WORLD_RID,
- 0, 0, 0, 0, 0, 0, 0, &worldid))
- atexit(free_world);
- else
- worldid = NULL;
- }
- if ((wanted & APR_FINFO_UPROT) && (finfo->valid & APR_FINFO_USER)) {
- ident.TrusteeType = TRUSTEE_IS_USER;
- ident.ptstrName = finfo->user;
- /* GetEffectiveRightsFromAcl isn't supported under Win9x,
- * which shouldn't come as a surprize. Since we are passing
- * TRUSTEE_IS_SID, always skip the A->W layer.
- */
- if (GetEffectiveRightsFromAclW(dacl, &ident, &acc) == ERROR_SUCCESS) {
- finfo->protection |= convert_prot(acc, prot_scope_user);
- finfo->valid |= APR_FINFO_UPROT;
- }
- }
- /* Windows NT: did not return group rights.
- * Windows 2000 returns group rights information.
- * Since WinNT kernels don't follow the unix model of
- * group associations, this all all pretty mute.
- */
- if ((wanted & APR_FINFO_GPROT) && (finfo->valid & APR_FINFO_GROUP)) {
- ident.TrusteeType = TRUSTEE_IS_GROUP;
- ident.ptstrName = finfo->group;
- if (GetEffectiveRightsFromAclW(dacl, &ident, &acc) == ERROR_SUCCESS) {
- finfo->protection |= convert_prot(acc, prot_scope_group);
- finfo->valid |= APR_FINFO_GPROT;
- }
- }
- if ((wanted & APR_FINFO_WPROT) && (worldid)) {
- ident.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
- ident.ptstrName = worldid;
- if (GetEffectiveRightsFromAclW(dacl, &ident, &acc) == ERROR_SUCCESS) {
- finfo->protection |= convert_prot(acc, prot_scope_world);
- finfo->valid |= APR_FINFO_WPROT;
- }
- }
- }
- static apr_status_t resolve_ident(apr_finfo_t *finfo, const char *fname,
- apr_int32_t wanted, apr_pool_t *pool)
- {
- apr_file_t *thefile = NULL;
- apr_status_t rv;
- /*
- * NT5 (W2K) only supports symlinks in the same manner as mount points.
- * This code should eventually take that into account, for now treat
- * every reparse point as a symlink...
- *
- * We must open the file with READ_CONTROL if we plan to retrieve the
- * user, group or permissions.
- */
-
- if ((rv = apr_file_open(&thefile, fname, APR_OPENINFO
- | ((wanted & APR_FINFO_LINK) ? APR_OPENLINK : 0)
- | ((wanted & (APR_FINFO_PROT | APR_FINFO_OWNER))
- ? APR_READCONTROL : 0),
- APR_OS_DEFAULT, pool)) == APR_SUCCESS) {
- rv = apr_file_info_get(finfo, wanted, thefile);
- finfo->filehand = NULL;
- apr_file_close(thefile);
- }
- else if (APR_STATUS_IS_EACCES(rv) && (wanted & (APR_FINFO_PROT
- | APR_FINFO_OWNER))) {
- /* We have a backup plan. Perhaps we couldn't grab READ_CONTROL?
- * proceed without asking for that permission...
- */
- if ((rv = apr_file_open(&thefile, fname, APR_OPENINFO
- | ((wanted & APR_FINFO_LINK) ? APR_OPENLINK : 0),
- APR_OS_DEFAULT, pool)) == APR_SUCCESS) {
- rv = apr_file_info_get(finfo, wanted & ~(APR_FINFO_PROT
- | APR_FINFO_OWNER),
- thefile);
- finfo->filehand = NULL;
- apr_file_close(thefile);
- }
- }
- if (rv != APR_SUCCESS && rv != APR_INCOMPLETE)
- return (rv);
- /* We picked up this case above and had opened the link's properties */
- if (wanted & APR_FINFO_LINK)
- finfo->valid |= APR_FINFO_LINK;
- return rv;
- }
- static void guess_protection_bits(apr_finfo_t *finfo)
- {
- /* Read, write execute for owner. In the Win9x environment, any
- * readable file is executable (well, not entirely 100% true, but
- * still looking for some cheap logic that would help us here.)
- * The same holds on NT if a file doesn't have a DACL (e.g., on FAT)
- */
- if (finfo->protection & APR_FREADONLY) {
- finfo->protection |= APR_WREAD | APR_WEXECUTE;
- }
- else {
- finfo->protection |= APR_WREAD | APR_WEXECUTE | APR_WWRITE;
- }
- finfo->protection |= (finfo->protection << prot_scope_group)
- | (finfo->protection << prot_scope_user);
- finfo->valid |= APR_FINFO_UPROT | APR_FINFO_GPROT | APR_FINFO_WPROT;
- }
- apr_status_t more_finfo(apr_finfo_t *finfo, const void *ufile,
- apr_int32_t wanted, int whatfile)
- {
- PSID user = NULL, grp = NULL;
- PACL dacl = NULL;
- apr_status_t rv;
- if (apr_os_level < APR_WIN_NT)
- guess_protection_bits(finfo);
- else if (wanted & (APR_FINFO_PROT | APR_FINFO_OWNER))
- {
- /* On NT this request is incredibly expensive, but accurate.
- * Since the WinNT-only functions below are protected by the
- * (apr_os_level < APR_WIN_NT) case above, we need no extra
- * tests, but remember GetNamedSecurityInfo & GetSecurityInfo
- * are not supported on 9x.
- */
- SECURITY_INFORMATION sinf = 0;
- PSECURITY_DESCRIPTOR pdesc = NULL;
- if (wanted & (APR_FINFO_USER | APR_FINFO_UPROT))
- sinf |= OWNER_SECURITY_INFORMATION;
- if (wanted & (APR_FINFO_GROUP | APR_FINFO_GPROT))
- sinf |= GROUP_SECURITY_INFORMATION;
- if (wanted & APR_FINFO_PROT)
- sinf |= DACL_SECURITY_INFORMATION;
- if (whatfile == MORE_OF_WFSPEC) {
- apr_wchar_t *wfile = (apr_wchar_t*) ufile;
- int fix = 0;
- if (wcsncmp(wfile, L"\\\\?\\", 4) == 0) {
- fix = 4;
- if (wcsncmp(wfile + fix, L"UNC\\", 4) == 0)
- wfile[6] = L'\\', fix = 6;
- }
- rv = GetNamedSecurityInfoW(wfile + fix,
- SE_FILE_OBJECT, sinf,
- ((wanted & (APR_FINFO_USER | APR_FINFO_UPROT)) ? &user : NULL),
- ((wanted & (APR_FINFO_GROUP | APR_FINFO_GPROT)) ? &grp : NULL),
- ((wanted & APR_FINFO_PROT) ? &dacl : NULL),
- NULL, &pdesc);
- if (fix == 6)
- wfile[6] = L'C';
- }
- else if (whatfile == MORE_OF_FSPEC)
- rv = GetNamedSecurityInfoA((char*)ufile,
- SE_FILE_OBJECT, sinf,
- ((wanted & (APR_FINFO_USER | APR_FINFO_UPROT)) ? &user : NULL),
- ((wanted & (APR_FINFO_GROUP | APR_FINFO_GPROT)) ? &grp : NULL),
- ((wanted & APR_FINFO_PROT) ? &dacl : NULL),
- NULL, &pdesc);
- else if (whatfile == MORE_OF_HANDLE)
- rv = GetSecurityInfo((HANDLE)ufile,
- SE_FILE_OBJECT, sinf,
- ((wanted & (APR_FINFO_USER | APR_FINFO_UPROT)) ? &user : NULL),
- ((wanted & (APR_FINFO_GROUP | APR_FINFO_GPROT)) ? &grp : NULL),
- ((wanted & APR_FINFO_PROT) ? &dacl : NULL),
- NULL, &pdesc);
- else
- return APR_INCOMPLETE;
- if (rv == ERROR_SUCCESS)
- apr_pool_cleanup_register(finfo->pool, pdesc, free_localheap,
- apr_pool_cleanup_null);
- else
- user = grp = dacl = NULL;
- if (user) {
- finfo->user = user;
- finfo->valid |= APR_FINFO_USER;
- }
- if (grp) {
- finfo->group = grp;
- finfo->valid |= APR_FINFO_GROUP;
- }
- if (dacl) {
- /* Retrieved the discresionary access list */
- resolve_prot(finfo, wanted, dacl);
- }
- else if (wanted & APR_FINFO_PROT)
- guess_protection_bits(finfo);
- }
- return ((wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS);
- }
- /* This generic fillin depends upon byhandle to be passed as 0 when
- * a WIN32_FILE_ATTRIBUTE_DATA or either WIN32_FIND_DATA [A or W] is
- * passed for wininfo. When the BY_HANDLE_FILE_INFORMATION structure
- * is passed for wininfo, byhandle is passed as 1 to offset the one
- * dword discrepancy in offset of the High/Low size structure members.
- *
- * The generic fillin returns 1 if the caller should further inquire
- * if this is a CHR filetype. If it's reasonably certain it can't be,
- * then the function returns 0.
- */
- int fillin_fileinfo(apr_finfo_t *finfo,
- WIN32_FILE_ATTRIBUTE_DATA *wininfo,
- int byhandle, apr_int32_t wanted)
- {
- DWORD *sizes = &wininfo->nFileSizeHigh + byhandle;
- int warn = 0;
- memset(finfo, '\0', sizeof(*finfo));
- FileTimeToAprTime(&finfo->atime, &wininfo->ftLastAccessTime);
- FileTimeToAprTime(&finfo->ctime, &wininfo->ftCreationTime);
- FileTimeToAprTime(&finfo->mtime, &wininfo->ftLastWriteTime);
- #if APR_HAS_LARGE_FILES
- finfo->size = (apr_off_t)sizes[1]
- | ((apr_off_t)sizes[0] << 32);
- #else
- finfo->size = (apr_off_t)sizes[1];
- if (finfo->size < 0 || sizes[0])
- finfo->size = 0x7fffffff;
- #endif
- if (wanted & APR_FINFO_LINK &&
- wininfo->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
- finfo->filetype = APR_LNK;
- }
- else if (wininfo->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
- finfo->filetype = APR_DIR;
- }
- else if (wininfo->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) {
- /* Warning: This test only succeeds on Win9x, on NT these files
- * (con, aux, nul, lpt#, com# etc) escape early detection!
- */
- finfo->filetype = APR_CHR;
- }
- else {
- /* Warning: Short of opening the handle to the file, the 'FileType'
- * appears to be unknowable (in any trustworthy or consistent sense)
- * on WinNT/2K as far as PIPE, CHR, etc are concerned.
- */
- if (!wininfo->ftLastWriteTime.dwLowDateTime
- && !wininfo->ftLastWriteTime.dwHighDateTime
- && !finfo->size)
- warn = 1;
- finfo->filetype = APR_REG;
- }
- /* The following flags are [for this moment] private to Win32.
- * That's the only excuse for not toggling valid bits to reflect them.
- */
- if (wininfo->dwFileAttributes & FILE_ATTRIBUTE_READONLY)
- finfo->protection = APR_FREADONLY;
-
- finfo->valid = APR_FINFO_ATIME | APR_FINFO_CTIME | APR_FINFO_MTIME
- | APR_FINFO_SIZE | APR_FINFO_TYPE; /* == APR_FINFO_MIN */
- /* Only byhandle optionally tests link targets, so tell that caller
- * what it wants to hear, otherwise the byattributes is never
- * reporting anything but the link.
- */
- if (!byhandle || (wanted & APR_FINFO_LINK))
- finfo->valid |= APR_FINFO_LINK;
- return warn;
- }
- APR_DECLARE(apr_status_t) apr_file_info_get(apr_finfo_t *finfo, apr_int32_t wanted,
- apr_file_t *thefile)
- {
- BY_HANDLE_FILE_INFORMATION FileInfo;
- if (thefile->buffered) {
- /* XXX: flush here is not mutex protected */
- apr_status_t rv = apr_file_flush(thefile);
- if (rv != APR_SUCCESS)
- return rv;
- }
- if (!GetFileInformationByHandle(thefile->filehand, &FileInfo)) {
- return apr_get_os_error();
- }
- fillin_fileinfo(finfo, (WIN32_FILE_ATTRIBUTE_DATA *) &FileInfo, 1, wanted);
- if (finfo->filetype == APR_REG)
- {
- /* Go the extra mile to be -certain- that we have a real, regular
- * file, since the attribute bits aren't a certain thing. Even
- * though fillin should have hinted if we *must* do this, we
- * don't need to take chances while the handle is already open.
- */
- DWORD FileType;
- if (FileType = GetFileType(thefile->filehand)) {
- if (FileType == FILE_TYPE_CHAR) {
- finfo->filetype = APR_CHR;
- }
- else if (FileType == FILE_TYPE_PIPE) {
- finfo->filetype = APR_PIPE;
- }
- /* Otherwise leave the original conclusion alone
- */
- }
- }
- finfo->pool = thefile->pool;
- /* ### The finfo lifetime may exceed the lifetime of thefile->pool
- * but finfo's aren't managed in pools, so where on earth would
- * we pstrdup the fname into???
- */
- finfo->fname = thefile->fname;
-
- /* Extra goodies known only by GetFileInformationByHandle() */
- finfo->inode = (apr_ino_t)FileInfo.nFileIndexLow
- | ((apr_ino_t)FileInfo.nFileIndexHigh << 32);
- finfo->device = FileInfo.dwVolumeSerialNumber;
- finfo->nlink = FileInfo.nNumberOfLinks;
- finfo->valid |= APR_FINFO_IDENT | APR_FINFO_NLINK;
- /* If we still want something more (besides the name) go get it!
- */
- if ((wanted &= ~finfo->valid) & ~APR_FINFO_NAME) {
- return more_finfo(finfo, thefile->filehand, wanted, MORE_OF_HANDLE);
- }
- return APR_SUCCESS;
- }
- APR_DECLARE(apr_status_t) apr_file_perms_set(const char *fname,
- apr_fileperms_t perms)
- {
- return APR_ENOTIMPL;
- }
- APR_DECLARE(apr_status_t) apr_stat(apr_finfo_t *finfo, const char *fname,
- apr_int32_t wanted, apr_pool_t *pool)
- {
- /* XXX: is constant - needs testing - which requires a lighter-weight root test fn */
- int isroot = 0;
- apr_status_t ident_rv = 0;
- apr_status_t rv;
- #if APR_HAS_UNICODE_FS
- apr_wchar_t wfname[APR_PATH_MAX];
- #endif
- char *filename = NULL;
- /* These all share a common subset of this structure */
- union {
- WIN32_FIND_DATAW w;
- WIN32_FIND_DATAA n;
- WIN32_FILE_ATTRIBUTE_DATA i;
- } FileInfo;
-
- /* Catch fname length == MAX_PATH since GetFileAttributesEx fails
- * with PATH_NOT_FOUND. We would rather indicate length error than
- * 'not found'
- */
- if (strlen(fname) >= APR_PATH_MAX) {
- return APR_ENAMETOOLONG;
- }
- #if APR_HAS_UNICODE_FS
- IF_WIN_OS_IS_UNICODE
- {
- if ((wanted & (APR_FINFO_IDENT | APR_FINFO_NLINK))
- || (~wanted & APR_FINFO_LINK)) {
- /* FindFirstFile and GetFileAttributesEx can't figure the inode,
- * device or number of links, so we need to resolve with an open
- * file handle. If the user has asked for these fields, fall over
- * to the get file info by handle method. If we fail, or the user
- * also asks for the file name, continue by our usual means.
- *
- * We also must use this method for a 'true' stat, that resolves
- * a symlink (NTFS Junction) target. This is because all fileinfo
- * on a Junction always returns the junction, opening the target
- * is the only way to resolve the target's attributes.
- */
- if ((ident_rv = resolve_ident(finfo, fname, wanted, pool))
- == APR_SUCCESS)
- return ident_rv;
- else if (ident_rv == APR_INCOMPLETE)
- wanted &= ~finfo->valid;
- }
- if (rv = utf8_to_unicode_path(wfname, sizeof(wfname)
- / sizeof(apr_wchar_t), fname))
- return rv;
- if (!(wanted & APR_FINFO_NAME)) {
- if (!GetFileAttributesExW(wfname, GetFileExInfoStandard,
- &FileInfo.i))
- return apr_get_os_error();
- }
- else {
- /* Guard against bogus wildcards and retrieve by name
- * since we want the true name, and set aside a long
- * enough string to handle the longest file name.
- */
- char tmpname[APR_FILE_MAX * 3 + 1];
- HANDLE hFind;
- if ((rv = test_safe_name(fname)) != APR_SUCCESS) {
- return rv;
- }
- hFind = FindFirstFileW(wfname, &FileInfo.w);
- if (hFind == INVALID_HANDLE_VALUE)
- return apr_get_os_error();
- FindClose(hFind);
- if (unicode_to_utf8_path(tmpname, sizeof(tmpname),
- FileInfo.w.cFileName)) {
- return APR_ENAMETOOLONG;
- }
- filename = apr_pstrdup(pool, tmpname);
- }
- }
- #endif
- #if APR_HAS_ANSI_FS
- ELSE_WIN_OS_IS_ANSI
- {
- char *root = NULL;
- const char *test = fname;
- rv = apr_filepath_root(&root, &test, APR_FILEPATH_NATIVE, pool);
- isroot = (root && *root && !(*test));
- if ((apr_os_level >= APR_WIN_98) && (!(wanted & APR_FINFO_NAME) || isroot))
- {
- /* cannot use FindFile on a Win98 root, it returns \*
- * GetFileAttributesExA is not available on Win95
- */
- if (!GetFileAttributesExA(fname, GetFileExInfoStandard,
- &FileInfo.i)) {
- return apr_get_os_error();
- }
- }
- else if (isroot) {
- /* This is Win95 and we are trying to stat a root. Lie.
- */
- if (GetDriveType(fname) != DRIVE_UNKNOWN)
- {
- finfo->pool = pool;
- finfo->filetype = 0;
- finfo->mtime = apr_time_now();
- finfo->protection |= APR_WREAD | APR_WEXECUTE | APR_WWRITE;
- finfo->protection |= (finfo->protection << prot_scope_group)
- | (finfo->protection << prot_scope_user);
- finfo->valid |= APR_FINFO_TYPE | APR_FINFO_PROT
- | APR_FINFO_MTIME
- | (wanted & APR_FINFO_LINK);
- return (wanted &= ~finfo->valid) ? APR_INCOMPLETE
- : APR_SUCCESS;
- }
- else
- return APR_FROM_OS_ERROR(ERROR_PATH_NOT_FOUND);
- }
- else {
- /* Guard against bogus wildcards and retrieve by name
- * since we want the true name, or are stuck in Win95,
- * or are looking for the root of a Win98 drive.
- */
- HANDLE hFind;
- if ((rv = test_safe_name(fname)) != APR_SUCCESS) {
- return rv;
- }
- hFind = FindFirstFileA(fname, &FileInfo.n);
- if (hFind == INVALID_HANDLE_VALUE) {
- return apr_get_os_error();
- }
- FindClose(hFind);
- filename = apr_pstrdup(pool, FileInfo.n.cFileName);
- }
- }
- #endif
- if (ident_rv != APR_INCOMPLETE) {
- if (fillin_fileinfo(finfo, (WIN32_FILE_ATTRIBUTE_DATA *) &FileInfo,
- 0, wanted))
- {
- /* Go the extra mile to assure we have a file. WinNT/2000 seems
- * to reliably translate char devices to the path '\\.\device'
- * so go ask for the full path.
- */
- if (apr_os_level >= APR_WIN_NT)
- {
- #if APR_HAS_UNICODE_FS
- apr_wchar_t tmpname[APR_FILE_MAX];
- apr_wchar_t *tmpoff = NULL;
- if (GetFullPathNameW(wfname, sizeof(tmpname) / sizeof(apr_wchar_t),
- tmpname, &tmpoff))
- {
- if (!wcsncmp(tmpname, L"\\\\.\\", 4)) {
- #else
- /* Same initial logic as above, but
- * only for WinNT/non-UTF-8 builds of APR:
- */
- char tmpname[APR_FILE_MAX];
- char *tmpoff;
- if (GetFullPathName(fname, sizeof(tmpname), tmpname, &tmpoff))
- {
- if (!strncmp(tmpname, "\\\\.\\", 4)) {
- #endif
- if (tmpoff == tmpname + 4) {
- finfo->filetype = APR_CHR;
- }
- /* For WHATEVER reason, CHR devices such as \\.\con
- * or \\.\lpt1 *may*not* update tmpoff; in fact the
- * resulting tmpoff is set to NULL. Guard against
- * either case.
- *
- * This code is identical for wide and narrow chars...
- */
- else if (!tmpoff) {
- tmpoff = tmpname + 4;
- while (*tmpoff) {
- if (*tmpoff == '\\' || *tmpoff == '/') {
- break;
- }
- ++tmpoff;
- }
- if (!*tmpoff) {
- finfo->filetype = APR_CHR;
- }
- }
- }
- }
- else {
- finfo->valid &= ~APR_FINFO_TYPE;
- }
- }
- else {
- finfo->valid &= ~APR_FINFO_TYPE;
- }
- }
- finfo->pool = pool;
- }
- if (filename && !isroot) {
- finfo->name = filename;
- finfo->valid |= APR_FINFO_NAME;
- }
- if (wanted &= ~finfo->valid) {
- /* Caller wants more than APR_FINFO_MIN | APR_FINFO_NAME */
- #if APR_HAS_UNICODE_FS
- if (apr_os_level >= APR_WIN_NT)
- return more_finfo(finfo, wfname, wanted, MORE_OF_WFSPEC);
- #endif
- return more_finfo(finfo, fname, wanted, MORE_OF_FSPEC);
- }
- return APR_SUCCESS;
- }
- APR_DECLARE(apr_status_t) apr_file_attrs_set(const char *fname,
- apr_fileattrs_t attributes,
- apr_fileattrs_t attr_mask,
- apr_pool_t *pool)
- {
- DWORD flags;
- apr_status_t rv;
- #if APR_HAS_UNICODE_FS
- apr_wchar_t wfname[APR_PATH_MAX];
- #endif
- /* Don't do anything if we can't handle the requested attributes */
- if (!(attr_mask & (APR_FILE_ATTR_READONLY
- | APR_FILE_ATTR_HIDDEN)))
- return APR_SUCCESS;
- #if APR_HAS_UNICODE_FS
- IF_WIN_OS_IS_UNICODE
- {
- if (rv = utf8_to_unicode_path(wfname,
- sizeof(wfname) / sizeof(wfname[0]),
- fname))
- return rv;
- flags = GetFileAttributesW(wfname);
- }
- #endif
- #if APR_HAS_ANSI_FS
- ELSE_WIN_OS_IS_ANSI
- {
- flags = GetFileAttributesA(fname);
- }
- #endif
- if (flags == 0xFFFFFFFF)
- return apr_get_os_error();
- if (attr_mask & APR_FILE_ATTR_READONLY)
- {
- if (attributes & APR_FILE_ATTR_READONLY)
- flags |= FILE_ATTRIBUTE_READONLY;
- else
- flags &= ~FILE_ATTRIBUTE_READONLY;
- }
- if (attr_mask & APR_FILE_ATTR_HIDDEN)
- {
- if (attributes & APR_FILE_ATTR_HIDDEN)
- flags |= FILE_ATTRIBUTE_HIDDEN;
- else
- flags &= ~FILE_ATTRIBUTE_HIDDEN;
- }
- #if APR_HAS_UNICODE_FS
- IF_WIN_OS_IS_UNICODE
- {
- rv = SetFileAttributesW(wfname, flags);
- }
- #endif
- #if APR_HAS_ANSI_FS
- ELSE_WIN_OS_IS_ANSI
- {
- rv = SetFileAttributesA(fname, flags);
- }
- #endif
- if (rv == 0)
- return apr_get_os_error();
- return APR_SUCCESS;
- }
- APR_DECLARE(apr_status_t) apr_file_mtime_set(const char *fname,
- apr_time_t mtime,
- apr_pool_t *pool)
- {
- apr_file_t *thefile;
- apr_status_t rv;
- rv = apr_file_open(&thefile, fname,
- APR_READ | APR_WRITEATTRS,
- APR_OS_DEFAULT, pool);
- if (!rv)
- {
- FILETIME file_ctime;
- FILETIME file_atime;
- FILETIME file_mtime;
- if (!GetFileTime(thefile->filehand,
- &file_ctime, &file_atime, &file_mtime))
- rv = apr_get_os_error();
- else
- {
- AprTimeToFileTime(&file_mtime, mtime);
- if (!SetFileTime(thefile->filehand,
- &file_ctime, &file_atime, &file_mtime))
- rv = apr_get_os_error();
- }
- apr_file_close(thefile);
- }
- return rv;
- }
|