Skip to content

Trailing dots and spaces in filenames are ignored #9227

Closed

Description

@cmb69

Description

The following code:

<?php

var_dump(realpath(__DIR__ . "/ntfs.txt."));
var_dump(file_put_contents(__DIR__ . "/ntfs.txt", "bar"));
var_dump(realpath(__DIR__ . "/ntfs.txt."));

Resulted in this output:

bool(false)
int(3)
string(35) "C:\php-sdk\phpdev\vs16\x64\ntfs.txt"

But I expected this output instead:

bool(false)
int(3)
bool(false)

This happens regardless of NTS or ZTS builds. The very same happens for trailing spaces. The basic problem is that Windows treats filenames with trailing spaces or dots in a special way, namely that these are basically ignored by most APIs. That article also mentions that leading spaces would be ignored, but I cannot reproduce this (Windows 21H1); maybe that has been changed in the meantime.

Anyhow, the current behavior is very confusing, and doesn't match other systems. Furthermore, there is actually code which is supposed to catch these cases (trailing spaces and dots), and to report ERROR_ACCESS_DENIED:

php-src/win32/ioutil.h

Lines 156 to 169 in 4679805

#define PHP_WIN32_IOUTIL_PATH_IS_OK_W(pathw, len) \
(!((len) >= 1 && L' ' == pathw[(len)-1] || \
(len) > 1 && !PHP_WIN32_IOUTIL_IS_SLASHW(pathw[(len)-2]) && L'.' != pathw[(len)-2] && L'.' == pathw[(len)-1]))
#define PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, ret, dealloc) do { \
size_t _len = wcslen(pathw); \
if (!PHP_WIN32_IOUTIL_PATH_IS_OK_W(pathw, _len)) { \
if (dealloc) { \
free((void *)pathw); \
} \
SET_ERRNO_FROM_WIN32_CODE(ERROR_ACCESS_DENIED); \
return ret; \
} \
} while (0);

However, this code is not exercised in many cases, since "normalization" occurs before the code is reached.

PHP Version

all supported versions

Operating System

Windows

Activity

  1. self-assigned this
    on Aug 2, 2022
  2. added a commit that references this issue on Aug 2, 2022
    e3eaa6a
  3. changed the title [-]Filenames with leading dots or spaces are not properly handled[/-] [+]Trailing dots and spaces in filenames are ignored[/+] on Aug 2, 2022
  4. jasperdevries commented on Jun 27, 2023

    @jasperdevries

    Sorry, first time reporter.
    We seem to have found another bug related to this, file_exists() still seems to ignore the trailing dot while realpath() does seem to work correctly.

    If we run the file below while the .env file exists

    <?php
    $filename = 'c:\path_to_file\.env.';
    
    var_dump(file_exists($filename));
    var_dump(realpath($filename));
    var_dump(file_get_contents($filename));
    

    we get the following results

    bool(true)
    bool(false)
    bool(false)
    

    while we would expect the output to be as followed

    bool(false)
    bool(false)
    bool(false)
    

    We're running this on php 8.2.3 on a windows server.

  5. iluuu1994 commented on Jun 27, 2023

    @iluuu1994
    Member

    @jasperdevries Thank you for your comment! This looks to be deliberate.

    I don't own a Windows machine and didn't try this, but the article linked above indicates that trailing periods are truncated when saving files on Windows.

    https://learn.microsoft.com/en-us/troubleshoot/windows-client/shell-experience/file-folder-name-whitespace-characters

    If a file is saved as 'Foo.txt.', where the trailing character(s) is an ASCII Period (0x2E), it will be saved to the file system as 'Foo.txt'.

    To avoid confusing scenarios like touch('.env.'); var_dump(file_exists('.env.')); /* false */, other file functions also support (and truncate) trailing periods. See https://github.com/php/php-src/blob/master/ext/standard/tests/file/gh9227.phpt.

  6. damianwadley commented on Jun 27, 2023

    @damianwadley
    Member

    Correct: as far as I've seen, Windows will drop trailing periods and spaces. Additionally, Explorer apparently drops leading spaces (not periods) when dealing with filenames, but you can get them through other methods.

    So if realpath isn't ignoring trailing periods anymore then presumably the other filesystem functions should be updated to match?

  7. PazkaL commented on Aug 29, 2023

    @PazkaL

    It looks like the file_exists function does accept the trailing period but all the other functions don't.
    This causes inconsistent behaviour on windows systems, which is visible if you look closely at the test provided by @jasperdevries

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions