"Fossies" - the Fresh Open Source Software Archive

Member "apr-1.7.0/file_io/win32/filestat.c" (20 Mar 2019, 31791 Bytes) of package /linux/www/apr-1.7.0.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "filestat.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.6.5_vs_1.7.0.

    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.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 apr_status_t guess_protection_bits(apr_finfo_t *finfo,
  192                                           apr_int32_t wanted)
  193 {
  194     /* Read, write execute for owner.  In the Win9x environment, any
  195      * readable file is executable (well, not entirely 100% true, but
  196      * still looking for some cheap logic that would help us here.)
  197      * The same holds on NT if a file doesn't have a DACL (e.g., on FAT)
  198      */
  199     if (finfo->protection & APR_FREADONLY) {
  200         finfo->protection |= APR_WREAD | APR_WEXECUTE;
  201     }
  202     else {
  203         finfo->protection |= APR_WREAD | APR_WEXECUTE | APR_WWRITE;
  204     }
  205     finfo->protection |= (finfo->protection << prot_scope_group)
  206                        | (finfo->protection << prot_scope_user);
  207 
  208     finfo->valid |= APR_FINFO_UPROT | APR_FINFO_GPROT | APR_FINFO_WPROT;
  209 
  210     return ((wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS);
  211 }
  212 
  213 static int reparse_point_is_link(WIN32_FILE_ATTRIBUTE_DATA *wininfo,
  214     int finddata, const char *fname)
  215 {
  216     int tag = 0;
  217 
  218     if (!(wininfo->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
  219     {
  220         return 0;
  221     }
  222 
  223     if (finddata)
  224     {
  225         // no matter A or W as we don't need file name
  226         tag = ((WIN32_FIND_DATAA*)wininfo)->dwReserved0;
  227     }
  228     else
  229     {
  230         if (test_safe_name(fname) != APR_SUCCESS) {
  231             return 0;
  232         }
  233 
  234 #if APR_HAS_UNICODE_FS
  235         IF_WIN_OS_IS_UNICODE
  236         {
  237             apr_wchar_t wfname[APR_PATH_MAX];
  238             HANDLE hFind;
  239             WIN32_FIND_DATAW fd;
  240 
  241             if (utf8_to_unicode_path(wfname, APR_PATH_MAX, fname) != APR_SUCCESS) {
  242                 return 0;
  243             }
  244 
  245             hFind = FindFirstFileW(wfname, &fd);
  246             if (hFind == INVALID_HANDLE_VALUE) {
  247                 return 0;
  248             }
  249 
  250             FindClose(hFind);
  251 
  252             tag = fd.dwReserved0;
  253         }
  254 #endif
  255 #if APR_HAS_ANSI_FS || 1
  256         ELSE_WIN_OS_IS_ANSI
  257         {
  258             HANDLE hFind;
  259             WIN32_FIND_DATAA fd;
  260 
  261             hFind = FindFirstFileA(fname, &fd);
  262             if (hFind == INVALID_HANDLE_VALUE) {
  263                 return 0;
  264             }
  265 
  266             FindClose(hFind);
  267 
  268             tag = fd.dwReserved0;
  269         }
  270 #endif
  271     }
  272 
  273     // Test "Name surrogate bit" to detect any kind of symbolic link
  274     // See https://docs.microsoft.com/en-us/windows/desktop/fileio/reparse-point-tags
  275     return tag & 0x20000000;
  276 }
  277 
  278 apr_status_t more_finfo(apr_finfo_t *finfo, const void *ufile, 
  279                         apr_int32_t wanted, int whatfile)
  280 {
  281     PSID user = NULL, grp = NULL;
  282     PACL dacl = NULL;
  283     apr_status_t rv;
  284 
  285     if (apr_os_level < APR_WIN_NT)
  286         return guess_protection_bits(finfo, wanted);
  287 
  288     if (wanted & (APR_FINFO_PROT | APR_FINFO_OWNER))
  289     {
  290         /* On NT this request is incredibly expensive, but accurate.
  291          * Since the WinNT-only functions below are protected by the
  292          * (apr_os_level < APR_WIN_NT) case above, we need no extra
  293          * tests, but remember GetNamedSecurityInfo & GetSecurityInfo
  294          * are not supported on 9x.
  295          */
  296         SECURITY_INFORMATION sinf = 0;
  297         PSECURITY_DESCRIPTOR pdesc = NULL;
  298         if (wanted & (APR_FINFO_USER | APR_FINFO_UPROT))
  299             sinf |= OWNER_SECURITY_INFORMATION;
  300         if (wanted & (APR_FINFO_GROUP | APR_FINFO_GPROT))
  301             sinf |= GROUP_SECURITY_INFORMATION;
  302         if (wanted & APR_FINFO_PROT)
  303             sinf |= DACL_SECURITY_INFORMATION;
  304         if (whatfile == MORE_OF_WFSPEC) {
  305             apr_wchar_t *wfile = (apr_wchar_t*) ufile;
  306             int fix = 0;
  307             if (wcsncmp(wfile, L"\\\\?\\", 4) == 0) {
  308                 fix = 4;
  309                 if (wcsncmp(wfile + fix, L"UNC\\", 4) == 0)
  310                     wfile[6] = L'\\', fix = 6;
  311             }
  312             rv = GetNamedSecurityInfoW(wfile + fix, 
  313                                  SE_FILE_OBJECT, sinf,
  314                                  ((wanted & (APR_FINFO_USER | APR_FINFO_UPROT)) ? &user : NULL),
  315                                  ((wanted & (APR_FINFO_GROUP | APR_FINFO_GPROT)) ? &grp : NULL),
  316                                  ((wanted & APR_FINFO_PROT) ? &dacl : NULL),
  317                                  NULL, &pdesc);
  318             if (fix == 6)
  319                 wfile[6] = L'C';
  320         }
  321         else if (whatfile == MORE_OF_FSPEC)
  322             rv = GetNamedSecurityInfoA((char*)ufile, 
  323                                  SE_FILE_OBJECT, sinf,
  324                                  ((wanted & (APR_FINFO_USER | APR_FINFO_UPROT)) ? &user : NULL),
  325                                  ((wanted & (APR_FINFO_GROUP | APR_FINFO_GPROT)) ? &grp : NULL),
  326                                  ((wanted & APR_FINFO_PROT) ? &dacl : NULL),
  327                                  NULL, &pdesc);
  328         else if (whatfile == MORE_OF_HANDLE)
  329             rv = GetSecurityInfo((HANDLE)ufile, 
  330                                  SE_FILE_OBJECT, sinf,
  331                                  ((wanted & (APR_FINFO_USER | APR_FINFO_UPROT)) ? &user : NULL),
  332                                  ((wanted & (APR_FINFO_GROUP | APR_FINFO_GPROT)) ? &grp : NULL),
  333                                  ((wanted & APR_FINFO_PROT) ? &dacl : NULL),
  334                                  NULL, &pdesc);
  335         else
  336             return APR_INCOMPLETE; /* should not occur */
  337         if (rv == ERROR_SUCCESS)
  338             apr_pool_cleanup_register(finfo->pool, pdesc, free_localheap, 
  339                                  apr_pool_cleanup_null);
  340         else
  341             user = grp = dacl = NULL;
  342 
  343         if (user) {
  344             finfo->user = user;
  345             finfo->valid |= APR_FINFO_USER;
  346         }
  347 
  348         if (grp) {
  349             finfo->group = grp;
  350             finfo->valid |= APR_FINFO_GROUP;
  351         }
  352 
  353         if (dacl) {
  354             /* Retrieved the discresionary access list */
  355             resolve_prot(finfo, wanted, dacl);
  356         }
  357         else if (wanted & APR_FINFO_PROT)
  358             guess_protection_bits(finfo, wanted);
  359     }
  360 
  361     if ((apr_os_level >= APR_WIN_2000) && (wanted & APR_FINFO_CSIZE)
  362                                        && (finfo->filetype == APR_REG))
  363     {
  364         DWORD sizelo, sizehi;
  365         if (whatfile == MORE_OF_HANDLE) {
  366             /* Not available for development and implementation under
  367              * a reasonable license; if you review the licensing
  368              * terms and conditions of;
  369              *   http://go.microsoft.com/fwlink/?linkid=84083
  370              * you probably understand why APR chooses not to implement.
  371              */
  372             IOSB sb;
  373             FSI fi;
  374             if ((ZwQueryInformationFile((HANDLE)ufile, &sb, 
  375                                        &fi, sizeof(fi), 5) == 0) 
  376                     && (sb.Status == 0)) {
  377                 finfo->csize = fi.AllocationSize;
  378                 finfo->valid |= APR_FINFO_CSIZE;
  379             }
  380         }
  381         else {
  382             SetLastError(NO_ERROR);
  383             if (whatfile == MORE_OF_WFSPEC)
  384                 sizelo = GetCompressedFileSizeW((apr_wchar_t*)ufile, &sizehi);
  385             else if (whatfile == MORE_OF_FSPEC)
  386                 sizelo = GetCompressedFileSizeA((char*)ufile, &sizehi);
  387             else
  388                 return APR_EGENERAL; /* should not occur */
  389         
  390             if (sizelo != INVALID_FILE_SIZE || GetLastError() == NO_ERROR) {
  391 #if APR_HAS_LARGE_FILES
  392                 finfo->csize =  (apr_off_t)sizelo
  393                              | ((apr_off_t)sizehi << 32);
  394 #else
  395                 finfo->csize = (apr_off_t)sizelo;
  396                 if (finfo->csize < 0 || sizehi)
  397                     finfo->csize = 0x7fffffff;
  398 #endif
  399                 finfo->valid |= APR_FINFO_CSIZE;
  400             }
  401         }
  402     }
  403     return ((wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS);
  404 }
  405 
  406 
  407 /* This generic fillin depends upon byhandle to be passed as 0 when
  408  * a WIN32_FILE_ATTRIBUTE_DATA or either WIN32_FIND_DATA [A or W] is
  409  * passed for wininfo.  When the BY_HANDLE_FILE_INFORMATION structure
  410  * is passed for wininfo, byhandle is passed as 1 to offset the one
  411  * dword discrepancy in offset of the High/Low size structure members.
  412  *
  413  * The generic fillin returns 1 if the caller should further inquire
  414  * if this is a CHR filetype.  If it's reasonably certain it can't be,
  415  * then the function returns 0.
  416  */
  417 int fillin_fileinfo(apr_finfo_t *finfo, 
  418                     WIN32_FILE_ATTRIBUTE_DATA *wininfo, 
  419                     int byhandle,
  420                     int finddata,
  421                     const char *fname,
  422                     apr_int32_t wanted)
  423 {
  424     DWORD *sizes = &wininfo->nFileSizeHigh + byhandle;
  425     int warn = 0;
  426 
  427     memset(finfo, '\0', sizeof(*finfo));
  428 
  429     FileTimeToAprTime(&finfo->atime, &wininfo->ftLastAccessTime);
  430     FileTimeToAprTime(&finfo->ctime, &wininfo->ftCreationTime);
  431     FileTimeToAprTime(&finfo->mtime, &wininfo->ftLastWriteTime);
  432 
  433 #if APR_HAS_LARGE_FILES
  434     finfo->size =  (apr_off_t)sizes[1]
  435                 | ((apr_off_t)sizes[0] << 32);
  436 #else
  437     finfo->size = (apr_off_t)sizes[1];
  438     if (finfo->size < 0 || sizes[0])
  439         finfo->size = 0x7fffffff;
  440 #endif
  441 
  442     if (wanted & APR_FINFO_LINK &&
  443         reparse_point_is_link(wininfo, finddata, fname)) {
  444         finfo->filetype = APR_LNK;
  445     }
  446     else if (wininfo->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  447         finfo->filetype = APR_DIR;
  448     }
  449     else if (wininfo->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) {
  450         /* Warning: This test only succeeds on Win9x, on NT these files
  451          * (con, aux, nul, lpt#, com# etc) escape early detection!
  452          */
  453         finfo->filetype = APR_CHR;
  454     }
  455     else {
  456         /* Warning: Short of opening the handle to the file, the 'FileType'
  457          * appears to be unknowable (in any trustworthy or consistent sense)
  458          * on WinNT/2K as far as PIPE, CHR, etc are concerned.
  459          */
  460         if (!wininfo->ftLastWriteTime.dwLowDateTime 
  461                 && !wininfo->ftLastWriteTime.dwHighDateTime 
  462                 && !finfo->size)
  463             warn = 1;
  464         finfo->filetype = APR_REG;
  465     }
  466 
  467     /* The following flags are [for this moment] private to Win32.
  468      * That's the only excuse for not toggling valid bits to reflect them.
  469      */
  470     if (wininfo->dwFileAttributes & FILE_ATTRIBUTE_READONLY)
  471         finfo->protection = APR_FREADONLY;
  472     
  473     finfo->valid = APR_FINFO_ATIME | APR_FINFO_CTIME | APR_FINFO_MTIME
  474                  | APR_FINFO_SIZE  | APR_FINFO_TYPE;   /* == APR_FINFO_MIN */
  475 
  476     /* Only byhandle optionally tests link targets, so tell that caller
  477      * what it wants to hear, otherwise the byattributes is never
  478      * reporting anything but the link.
  479      */
  480     if (!byhandle || (wanted & APR_FINFO_LINK))
  481         finfo->valid |= APR_FINFO_LINK;
  482     return warn;
  483 }
  484 
  485 
  486 APR_DECLARE(apr_status_t) apr_file_info_get(apr_finfo_t *finfo, apr_int32_t wanted,
  487                                             apr_file_t *thefile)
  488 {
  489     BY_HANDLE_FILE_INFORMATION FileInfo;
  490 
  491     if (thefile->buffered) {
  492         /* XXX: flush here is not mutex protected */
  493         apr_status_t rv = apr_file_flush(thefile);
  494         if (rv != APR_SUCCESS)
  495             return rv;
  496     }
  497 
  498     /* GetFileInformationByHandle() is implemented via two syscalls:
  499      * QueryInformationVolume and QueryAllInformationFile. Use cheaper
  500      * GetFileSizeEx() API if we only need to get the file size. */
  501     if (wanted == APR_FINFO_SIZE) {
  502        LARGE_INTEGER size;
  503 
  504        if (!GetFileSizeEx(thefile->filehand, &size)) {
  505           return apr_get_os_error();
  506        }
  507 
  508        finfo->pool = thefile->pool;
  509        finfo->fname = thefile->fname;
  510        finfo->size = size.QuadPart;
  511        finfo->valid = APR_FINFO_SIZE;
  512 
  513        return APR_SUCCESS;
  514     }
  515 
  516     if (!GetFileInformationByHandle(thefile->filehand, &FileInfo)) {
  517         return apr_get_os_error();
  518     }
  519 
  520     fillin_fileinfo(finfo, (WIN32_FILE_ATTRIBUTE_DATA *) &FileInfo, 1, 0, thefile->fname, wanted);
  521 
  522     if (finfo->filetype == APR_REG)
  523     {
  524         /* Go the extra mile to be -certain- that we have a real, regular
  525          * file, since the attribute bits aren't a certain thing.  Even
  526          * though fillin should have hinted if we *must* do this, we
  527          * don't need to take chances while the handle is already open.
  528          */
  529         DWORD FileType;
  530         if ((FileType = GetFileType(thefile->filehand))) {
  531             if (FileType == FILE_TYPE_CHAR) {
  532                 finfo->filetype = APR_CHR;
  533             }
  534             else if (FileType == FILE_TYPE_PIPE) {
  535                 finfo->filetype = APR_PIPE;
  536             }
  537             /* Otherwise leave the original conclusion alone 
  538              */
  539         }
  540     }
  541 
  542     finfo->pool = thefile->pool;
  543 
  544     /* ### The finfo lifetime may exceed the lifetime of thefile->pool
  545      * but finfo's aren't managed in pools, so where on earth would
  546      * we pstrdup the fname into???
  547      */
  548     finfo->fname = thefile->fname;
  549  
  550     /* Extra goodies known only by GetFileInformationByHandle() */
  551     finfo->inode  =  (apr_ino_t)FileInfo.nFileIndexLow
  552                   | ((apr_ino_t)FileInfo.nFileIndexHigh << 32);
  553     finfo->device = FileInfo.dwVolumeSerialNumber;
  554     finfo->nlink  = FileInfo.nNumberOfLinks;
  555 
  556     finfo->valid |= APR_FINFO_IDENT | APR_FINFO_NLINK;
  557 
  558     /* If we still want something more (besides the name) go get it! 
  559      */
  560     if ((wanted &= ~finfo->valid) & ~APR_FINFO_NAME) {
  561         return more_finfo(finfo, thefile->filehand, wanted, MORE_OF_HANDLE);
  562     }
  563 
  564     return APR_SUCCESS;
  565 }
  566 
  567 APR_DECLARE(apr_status_t) apr_file_perms_set(const char *fname,
  568                                            apr_fileperms_t perms)
  569 {
  570     return APR_ENOTIMPL;
  571 }
  572 
  573 APR_DECLARE(apr_status_t) apr_stat(apr_finfo_t *finfo, const char *fname,
  574                                    apr_int32_t wanted, apr_pool_t *pool)
  575 {
  576     /* XXX: is constant - needs testing - which requires a lighter-weight root test fn */
  577     int isroot = 0;
  578     apr_status_t ident_rv = 0;
  579     apr_status_t rv;
  580 #if APR_HAS_UNICODE_FS
  581     apr_wchar_t wfname[APR_PATH_MAX];
  582 
  583 #endif
  584     char *filename = NULL;
  585     /* These all share a common subset of this structure */
  586     union {
  587         WIN32_FIND_DATAW w;
  588         WIN32_FIND_DATAA n;
  589         WIN32_FILE_ATTRIBUTE_DATA i;
  590     } FileInfo;
  591     int finddata = 0;
  592     
  593     /* Catch fname length == MAX_PATH since GetFileAttributesEx fails 
  594      * with PATH_NOT_FOUND.  We would rather indicate length error than 
  595      * 'not found'
  596      */        
  597     if (strlen(fname) >= APR_PATH_MAX) {
  598         return APR_ENAMETOOLONG;
  599     }
  600 
  601 #if APR_HAS_UNICODE_FS
  602     IF_WIN_OS_IS_UNICODE
  603     {
  604         if ((wanted & (APR_FINFO_IDENT | APR_FINFO_NLINK)) 
  605                || (~wanted & APR_FINFO_LINK)) {
  606             /* FindFirstFile and GetFileAttributesEx can't figure the inode,
  607              * device or number of links, so we need to resolve with an open 
  608              * file handle.  If the user has asked for these fields, fall over 
  609              * to the get file info by handle method.  If we fail, or the user
  610              * also asks for the file name, continue by our usual means.
  611              *
  612              * We also must use this method for a 'true' stat, that resolves
  613              * a symlink (NTFS Junction) target.  This is because all fileinfo
  614              * on a Junction always returns the junction, opening the target
  615              * is the only way to resolve the target's attributes.
  616              */
  617             if ((ident_rv = resolve_ident(finfo, fname, wanted, pool)) 
  618                     == APR_SUCCESS)
  619                 return ident_rv;
  620             else if (ident_rv == APR_INCOMPLETE)
  621                 wanted &= ~finfo->valid;
  622         }
  623 
  624         if ((rv = utf8_to_unicode_path(wfname, sizeof(wfname) 
  625                                             / sizeof(apr_wchar_t), fname)))
  626             return rv;
  627         if (!(wanted & (APR_FINFO_NAME | APR_FINFO_LINK))) {
  628             if (!GetFileAttributesExW(wfname, GetFileExInfoStandard, 
  629                                       &FileInfo.i))
  630                 return apr_get_os_error();
  631         }
  632         else {
  633             /* Guard against bogus wildcards and retrieve by name
  634              * since we want the true name, and set aside a long
  635              * enough string to handle the longest file name.
  636              */
  637             HANDLE hFind;
  638             if ((rv = test_safe_name(fname)) != APR_SUCCESS) {
  639                 return rv;
  640             }
  641             hFind = FindFirstFileW(wfname, &FileInfo.w);
  642             if (hFind == INVALID_HANDLE_VALUE)
  643                 return apr_get_os_error();
  644             FindClose(hFind);
  645             finddata = 1;
  646 
  647             if (wanted & APR_FINFO_NAME)
  648             {
  649                 char tmpname[APR_FILE_MAX * 3 + 1];
  650                 if (unicode_to_utf8_path(tmpname, sizeof(tmpname),
  651                                          FileInfo.w.cFileName)) {
  652                     return APR_ENAMETOOLONG;
  653                 }
  654                 filename = apr_pstrdup(pool, tmpname);
  655             }
  656         }
  657     }
  658 #endif
  659 #if APR_HAS_ANSI_FS
  660     ELSE_WIN_OS_IS_ANSI
  661     {
  662         char *root = NULL;
  663         const char *test = fname;
  664         rv = apr_filepath_root(&root, &test, APR_FILEPATH_NATIVE, pool);
  665         isroot = (root && *root && !(*test));
  666 
  667         if ((apr_os_level >= APR_WIN_98) && (!(wanted & (APR_FINFO_NAME | APR_FINFO_LINK)) || isroot))
  668         {
  669             /* cannot use FindFile on a Win98 root, it returns \*
  670              * GetFileAttributesExA is not available on Win95
  671              */
  672             if (!GetFileAttributesExA(fname, GetFileExInfoStandard, 
  673                                      &FileInfo.i)) {
  674                 return apr_get_os_error();
  675             }
  676         }
  677         else if (isroot) {
  678             /* This is Win95 and we are trying to stat a root.  Lie.
  679              */
  680             if (GetDriveType(fname) != DRIVE_UNKNOWN) 
  681             {
  682                 finfo->pool = pool;
  683                 finfo->filetype = 0;
  684                 finfo->mtime = apr_time_now();
  685                 finfo->protection |= APR_WREAD | APR_WEXECUTE | APR_WWRITE;
  686                 finfo->protection |= (finfo->protection << prot_scope_group) 
  687                                    | (finfo->protection << prot_scope_user);
  688                 finfo->valid |= APR_FINFO_TYPE | APR_FINFO_PROT 
  689                               | APR_FINFO_MTIME
  690                               | (wanted & APR_FINFO_LINK);
  691                 return (wanted &= ~finfo->valid) ? APR_INCOMPLETE 
  692                                                  : APR_SUCCESS;
  693             }
  694             else
  695                 return APR_FROM_OS_ERROR(ERROR_PATH_NOT_FOUND);
  696         }
  697         else  {
  698             /* Guard against bogus wildcards and retrieve by name
  699              * since we want the true name, or are stuck in Win95,
  700              * or are looking for the root of a Win98 drive.
  701              */
  702             HANDLE hFind;
  703             if ((rv = test_safe_name(fname)) != APR_SUCCESS) {
  704                 return rv;
  705             }
  706             hFind = FindFirstFileA(fname, &FileInfo.n);
  707             if (hFind == INVALID_HANDLE_VALUE) {
  708                 return apr_get_os_error();
  709             } 
  710             FindClose(hFind);
  711             finddata = 1;
  712             if (wanted & APR_FINFO_NAME) {
  713                 filename = apr_pstrdup(pool, FileInfo.n.cFileName);
  714             }
  715         }
  716     }
  717 #endif
  718 
  719     if (ident_rv != APR_INCOMPLETE) {
  720         if (fillin_fileinfo(finfo, (WIN32_FILE_ATTRIBUTE_DATA *) &FileInfo, 
  721                             0, finddata, fname, wanted))
  722         {
  723             /* Go the extra mile to assure we have a file.  WinNT/2000 seems
  724              * to reliably translate char devices to the path '\\.\device'
  725              * so go ask for the full path.
  726              */
  727             if (apr_os_level >= APR_WIN_NT)
  728             {
  729 #if APR_HAS_UNICODE_FS
  730                 apr_wchar_t tmpname[APR_FILE_MAX];
  731                 apr_wchar_t *tmpoff = NULL;
  732                 if (GetFullPathNameW(wfname, sizeof(tmpname) / sizeof(apr_wchar_t),
  733                                      tmpname, &tmpoff))
  734                 {
  735                     if (!wcsncmp(tmpname, L"\\\\.\\", 4)) {
  736 #else
  737                 /* Same initial logic as above, but
  738                  * only for WinNT/non-UTF-8 builds of APR:
  739                  */
  740                 char tmpname[APR_FILE_MAX];
  741                 char *tmpoff;
  742                 if (GetFullPathName(fname, sizeof(tmpname), tmpname, &tmpoff))
  743                 {
  744                     if (!strncmp(tmpname, "\\\\.\\", 4)) {
  745 #endif
  746                         if (tmpoff == tmpname + 4) {
  747                             finfo->filetype = APR_CHR;
  748                         }
  749                         /* For WHATEVER reason, CHR devices such as \\.\con 
  750                          * or \\.\lpt1 *may*not* update tmpoff; in fact the
  751                          * resulting tmpoff is set to NULL.  Guard against 
  752                          * either case.
  753                          *
  754                          * This code is identical for wide and narrow chars...
  755                          */
  756                         else if (!tmpoff) {
  757                             tmpoff = tmpname + 4;
  758                             while (*tmpoff) {
  759                                 if (*tmpoff == '\\' || *tmpoff == '/') {
  760                                     break;
  761                                 }
  762                                 ++tmpoff;
  763                             }
  764                             if (!*tmpoff) {
  765                                 finfo->filetype = APR_CHR;
  766                             }
  767                         }
  768                     }
  769                 }
  770                 else {
  771                     finfo->valid &= ~APR_FINFO_TYPE;
  772                 }
  773 
  774             }
  775             else {
  776                 finfo->valid &= ~APR_FINFO_TYPE;
  777             }
  778         }
  779         finfo->pool = pool;
  780     }
  781 
  782     if (filename && !isroot) {
  783         finfo->name = filename;
  784         finfo->valid |= APR_FINFO_NAME;
  785     }
  786 
  787     if (wanted &= ~finfo->valid) {
  788         /* Caller wants more than APR_FINFO_MIN | APR_FINFO_NAME */
  789 #if APR_HAS_UNICODE_FS
  790         if (apr_os_level >= APR_WIN_NT)
  791             return more_finfo(finfo, wfname, wanted, MORE_OF_WFSPEC);
  792 #endif
  793         return more_finfo(finfo, fname, wanted, MORE_OF_FSPEC);
  794     }
  795 
  796     return APR_SUCCESS;
  797 }
  798 
  799 APR_DECLARE(apr_status_t) apr_file_attrs_set(const char *fname,
  800                                              apr_fileattrs_t attributes,
  801                                              apr_fileattrs_t attr_mask,
  802                                              apr_pool_t *pool)
  803 {
  804     DWORD flags;
  805     apr_status_t rv;
  806 #if APR_HAS_UNICODE_FS
  807     apr_wchar_t wfname[APR_PATH_MAX];
  808 #endif
  809 
  810     /* Don't do anything if we can't handle the requested attributes */
  811     if (!(attr_mask & (APR_FILE_ATTR_READONLY
  812                        | APR_FILE_ATTR_HIDDEN)))
  813         return APR_SUCCESS;
  814 
  815 #if APR_HAS_UNICODE_FS
  816     IF_WIN_OS_IS_UNICODE
  817     {
  818         if ((rv = utf8_to_unicode_path(wfname,
  819                                        sizeof(wfname) / sizeof(wfname[0]),
  820                                        fname)))
  821             return rv;
  822         flags = GetFileAttributesW(wfname);
  823     }
  824 #endif
  825 #if APR_HAS_ANSI_FS
  826     ELSE_WIN_OS_IS_ANSI
  827     {
  828         flags = GetFileAttributesA(fname);
  829     }
  830 #endif
  831 
  832     if (flags == 0xFFFFFFFF)
  833         return apr_get_os_error();
  834 
  835     if (attr_mask & APR_FILE_ATTR_READONLY)
  836     {
  837         if (attributes & APR_FILE_ATTR_READONLY)
  838             flags |= FILE_ATTRIBUTE_READONLY;
  839         else
  840             flags &= ~FILE_ATTRIBUTE_READONLY;
  841     }
  842 
  843     if (attr_mask & APR_FILE_ATTR_HIDDEN)
  844     {
  845         if (attributes & APR_FILE_ATTR_HIDDEN)
  846             flags |= FILE_ATTRIBUTE_HIDDEN;
  847         else
  848             flags &= ~FILE_ATTRIBUTE_HIDDEN;
  849     }
  850 
  851 #if APR_HAS_UNICODE_FS
  852     IF_WIN_OS_IS_UNICODE
  853     {
  854         rv = SetFileAttributesW(wfname, flags);
  855     }
  856 #endif
  857 #if APR_HAS_ANSI_FS
  858     ELSE_WIN_OS_IS_ANSI
  859     {
  860         rv = SetFileAttributesA(fname, flags);
  861     }
  862 #endif
  863 
  864     if (rv == 0)
  865         return apr_get_os_error();
  866 
  867     return APR_SUCCESS;
  868 }
  869 
  870 
  871 APR_DECLARE(apr_status_t) apr_file_mtime_set(const char *fname,
  872                                              apr_time_t mtime,
  873                                              apr_pool_t *pool)
  874 {
  875     apr_file_t *thefile;
  876     apr_status_t rv;
  877 
  878     rv = apr_file_open(&thefile, fname,
  879                        APR_FOPEN_READ | APR_WRITEATTRS,
  880                        APR_OS_DEFAULT, pool);
  881     if (!rv)
  882     {
  883         FILETIME file_ctime;
  884         FILETIME file_atime;
  885         FILETIME file_mtime;
  886 
  887         if (!GetFileTime(thefile->filehand,
  888                          &file_ctime, &file_atime, &file_mtime))
  889             rv = apr_get_os_error();
  890         else
  891         {
  892             AprTimeToFileTime(&file_mtime, mtime);
  893             if (!SetFileTime(thefile->filehand,
  894                              &file_ctime, &file_atime, &file_mtime))
  895                 rv = apr_get_os_error();
  896         }
  897 
  898         apr_file_close(thefile);
  899     }
  900 
  901     return rv;
  902 }