Skip to content

win32: Fix enumerate root dir content on SFTP-server #148

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion contrib/win32/win32compat/fileio.c
Original file line number Diff line number Diff line change
Expand Up @@ -745,12 +745,21 @@ fileio_stat(const char *path, struct _stat64 *buf)
wchar_t* wpath = NULL;
WIN32_FILE_ATTRIBUTE_DATA attributes = { 0 };
int ret = -1, len = 0;

memset(buf, 0, sizeof(struct _stat64));

/* Detect root dir */
if (path && strcmp(path, "/") == 0) {
Copy link

@bagajjal bagajjal May 24, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from winscp (or) any other third party software.. if we receive "/" then it is treated as the root dir....

But I am not sure if all the thirdparty softwares follow the same notion.. not sure if some of them can use \ instead of "/", is it possible.. is there a standard notion?

image

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested this PR in WinSCP and Bitvise SFTP-clients.

buf->st_mode = _S_IFDIR | _S_IREAD | 0xFF;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0xff is not required here?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can also be removed..

buf->st_dev = USHRT_MAX; // rootdir flag

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use ATTR_ROOTDIR to be consistent..

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ATTR_ROOTDIR is 32 bit
buf->st_dev is 16 bit

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the need for setting this here as we are not using this any where else?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can remove this line, if it so cuts your eyes.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should remove the unnecessary code so its easy to maintain the long run..

return 0;
}

if ((wpath = utf8_to_utf16(path)) == NULL) {
errno = errno_from_Win32LastError();
debug3("utf8_to_utf16 failed for file:%s error:%d", path, GetLastError());
return -1;
}
memset(buf, 0, sizeof(struct _stat64));

if (GetFileAttributesExW(wpath, GetFileExInfoStandard, &attributes) == FALSE) {
errno = errno_from_Win32LastError();
Expand Down
16 changes: 13 additions & 3 deletions contrib/win32/win32compat/misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,16 @@ convertToBackslash(char *str)
}
}

void
convertToBackslashW(wchar_t *str)
{
while (*str) {
if (*str == L'/')
*str = L'\\';
str++;
}
}

/* convert back slash to forward slash */
void
convertToForwardslash(char *str)
Expand All @@ -874,9 +884,9 @@ realpath(const char *path, char resolved[PATH_MAX])
char tempPath[PATH_MAX];

if ((path[0] == '/') && path[1] && (path[2] == ':'))
strncpy(resolved, path + 1, strlen(path)); /* skip the first '/' */
strncpy(resolved, path + 1, PATH_MAX); /* skip the first '/' */
else
strncpy(resolved, path, strlen(path) + 1);
strncpy(resolved, path, PATH_MAX);

if ((resolved[0]) && (resolved[1] == ':') && (resolved[2] == '\0')) { /* make "x:" as "x:\\" */
resolved[2] = '\\';
Expand All @@ -889,7 +899,7 @@ realpath(const char *path, char resolved[PATH_MAX])
convertToForwardslash(tempPath);

resolved[0] = '/'; /* will be our first slash in /x:/users/test1 format */
strncpy(resolved + 1, tempPath, sizeof(tempPath) - 1);
strncpy(resolved + 1, tempPath, PATH_MAX - 1);
return resolved;
}

Expand Down
7 changes: 7 additions & 0 deletions contrib/win32/win32compat/misc_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
#define SSH_ASYNC_STDOUT "SSH_ASYNC_STDOUT"
#define SSH_ASYNC_STDERR "SSH_ASYNC_STDERR"

#define GOTO_CLEANUP_IF(_cond_,_err_) do { \
if ((_cond_)) { \
hr = _err_; \
goto cleanup; \
} \
} while(0)
#define NULL_DEVICE "/dev/null"

#define IS_INVALID_HANDLE(h) ( ((NULL == h) || (INVALID_HANDLE_VALUE == h)) ? 1 : 0 )
Expand All @@ -17,6 +23,7 @@ void w32posix_done();
char* w32_programdir();

void convertToBackslash(char *str);
void convertToBackslashW(wchar_t *str);
void convertToForwardslash(char *str);

void unix_time_to_file_time(ULONG, LPFILETIME);
Expand Down
130 changes: 126 additions & 4 deletions contrib/win32/win32compat/win32_dirent.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,54 @@ struct DIR_ {
intptr_t hFile;
struct _wfinddata_t c_file;
int first;
wchar_t * nextdisk;
};

#define ATTR_ROOTDIR UINT_MAX

/* Enumerate all devices which have drive name.
Return a DIR stream on the root directory, or NULL if it could not be enumerated. */
DIR *
openrootdir(const char *name)
{
int hr = 0;
DWORD dw;
DIR * pdir;
struct _wfinddata_t c_file = {0};
wchar_t * p;

dw = GetLogicalDriveStringsW(_countof(c_file.name) - 2, c_file.name);
if (!dw) {
errno = ENODEV;
return NULL;
}
c_file.attrib = ATTR_ROOTDIR;
c_file.size = 0;
p = c_file.name;
while (*p) {
size_t len = wcslen(p);
if (len == 0)
break;
p += len + 1;
c_file.size++;
}
if (c_file.size == 0) {
errno = ENODEV;
return NULL;
}
pdir = malloc(sizeof(DIR));
if (!pdir) {
errno = ENOMEM;
return NULL;
}
memset(pdir, 0, sizeof(DIR));
pdir->hFile = 0;
memcpy(&pdir->c_file, &c_file, sizeof(c_file));
pdir->first = 1;

return pdir;
}

/* Open a directory stream on NAME.
Return a DIR stream on the directory, or NULL if it could not be opened. */
DIR *
Expand All @@ -55,14 +101,31 @@ opendir(const char *name)
wchar_t searchstr[PATH_MAX];
wchar_t* wname = NULL;
int needed;
size_t len;

/* Detect root dir */
if (name && strcmp(name, "/") == 0)
return openrootdir(name);

if ((wname = utf8_to_utf16(sanitized_path(name))) == NULL) {
errno = ENOMEM;
return NULL;
}

convertToBackslashW(wname);
len = wcslen(wname);
if (len && wname[len-1] == L'\\') {
len--;
wname[len] = 0;
}
if (len >= PATH_MAX) {
free(wname);
errno = ENAMETOOLONG;
return NULL;
}

/* add *.* for Windows _findfirst() search pattern */
swprintf_s(searchstr, PATH_MAX, L"%s\\*.*", wname);
swprintf_s(searchstr, _countof(searchstr) - 1, L"%s\\*.*", wname);
free(wname);

if ((hFile = _wfindfirst(searchstr, &c_file)) == -1L)
Expand Down Expand Up @@ -92,13 +155,69 @@ closedir(DIR *dirp)

if (dirp && (dirp->hFile)) {
_findclose(dirp->hFile);
dirp->hFile = 0;
free(dirp);
}
free(dirp);

return 0;
}

/* Read a root directory entry from DIRP.
Return a pointer to a `struct dirent' describing the entry,
or NULL for EOF or error. The storage returned may be overwritten
by a later readdir call on the same DIR stream. */
struct dirent *
readrootdir(DIR * dirp)
{
wchar_t * p;
size_t len = 0;
struct dirent *pdirentry;
UINT dt;
ULARGE_INTEGER totalNumberOfBytes;
BOOL x;

if (dirp->c_file.size <= 0) {
errno = ENODATA;
return NULL;
}
if (dirp->first) {
dirp->first = 0;
dirp->nextdisk = dirp->c_file.name;
}

p = dirp->nextdisk;

for ( ; ; p += len + 1) {
len = wcslen(p);
if (len == 0) {
dirp->nextdisk = p;
errno = ENODATA;
return NULL; /* end of multi-string */
}

dt = GetDriveTypeW(p);
if (dt == DRIVE_UNKNOWN || dt == DRIVE_NO_ROOT_DIR || dt == DRIVE_RAMDISK)
continue;

x = GetDiskFreeSpaceExW(p, NULL, &totalNumberOfBytes, NULL);
if (!x || totalNumberOfBytes.QuadPart == 0)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, we can remove "totalNumberOfBytes.QuadPart == 0"

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Var totalNumberOfBytes.QuadPart equ 0 when CD-drive have not CD-disk.

continue;

break; // process filtered disk
}
dirp->nextdisk = p + len + 1;

if ((pdirentry = malloc(sizeof(struct dirent))) == NULL) {
errno = ENOMEM;
return NULL;
}
pdirentry->d_name[0] = (char)p[0];
pdirentry->d_name[1] = ':';
pdirentry->d_name[2] = 0;

pdirentry->d_ino = 1; // a fictious one like UNIX to say it is nonzero
return pdirentry;
}

/* Read a directory entry from DIRP.
Return a pointer to a `struct dirent' describing the entry,
or NULL for EOF or error. The storage returned may be overwritten
Expand All @@ -113,6 +232,9 @@ readdir(void *avp)
DIR *dirp = (DIR *)avp;
char *tmp = NULL;

if (dirp->hFile == 0 && dirp->c_file.attrib == ATTR_ROOTDIR)
return readrootdir(dirp);

for (;;) {
if (dirp->first) {
memcpy(&c_file, &dirp->c_file, sizeof(c_file));
Expand All @@ -128,7 +250,7 @@ readdir(void *avp)
return NULL;
}

strncpy(pdirentry.d_name, tmp, strlen(tmp) + 1);
strncpy(pdirentry.d_name, tmp, sizeof(pdirentry.d_name));
free(tmp);

pdirentry.d_ino = 1; /* a fictious one like UNIX to say it is nonzero */
Expand Down