Skip to content

Commit

Permalink
Implement fstatat flag: AT_EMPTY_PATH
Browse files Browse the repository at this point in the history
Also, add new test for fstatat.

I notived this issue while updating to the new musl version that
uses this internally.
  • Loading branch information
sbc100 committed Jan 19, 2021
1 parent ee483fc commit 385a4b3
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 20 deletions.
35 changes: 21 additions & 14 deletions src/library_syscall.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,26 @@ var SyscallsLibrary = {
umask: 0x1FF, // S_IRWXU | S_IRWXG | S_IRWXO

// shared utilities
calculateAt: function(dirfd, path) {
if (path[0] !== '/') {
// relative path
var dir;
if (dirfd === {{{ cDefine('AT_FDCWD') }}}) {
dir = FS.cwd();
} else {
var dirstream = FS.getStream(dirfd);
if (!dirstream) throw new FS.ErrnoError({{{ cDefine('EBADF') }}});
dir = dirstream.path;
calculateAt: function(dirfd, path, allowEmpty) {
if (path[0] === '/') {
return path;
}
// relative path
var dir;
if (dirfd === {{{ cDefine('AT_FDCWD') }}}) {
dir = FS.cwd();
} else {
var dirstream = FS.getStream(dirfd);
if (!dirstream) throw new FS.ErrnoError({{{ cDefine('EBADF') }}});
dir = dirstream.path;
}
if (path.length == 0) {
if (!allowEmpty) {
throw new FS.ErrnoError({{{ cDefine('ENOENT') }}});;
}
path = PATH.join2(dir, path);
return dir;
}
return path;
return PATH.join2(dir, path);
},

doStat: function(func, path, buf) {
Expand Down Expand Up @@ -1269,11 +1275,12 @@ var SyscallsLibrary = {
__sys_fstatat64: function(dirfd, path, buf, flags) {
path = SYSCALLS.getStr(path);
var nofollow = flags & {{{ cDefine('AT_SYMLINK_NOFOLLOW') }}};
flags = flags & (~{{{ cDefine('AT_SYMLINK_NOFOLLOW') }}});
var allowEmpty = flags & {{{ cDefine('AT_EMPTY_PATH') }}};
flags = flags & (~{{{ cDefine('AT_SYMLINK_NOFOLLOW') | cDefine('AT_EMPTY_PATH') }}});
#if ASSERTIONS
assert(!flags, flags);
#endif
path = SYSCALLS.calculateAt(dirfd, path);
path = SYSCALLS.calculateAt(dirfd, path, allowEmpty);
return SYSCALLS.doStat(nofollow ? FS.lstat : FS.stat, path, buf);
},
__sys_unlinkat: function(dirfd, path, flags) {
Expand Down
3 changes: 2 additions & 1 deletion src/struct_info.json
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@
"O_WRONLY",
"AT_FDCWD",
"AT_SYMLINK_NOFOLLOW",
"AT_REMOVEDIR"
"AT_REMOVEDIR",
"AT_EMPTY_PATH"
],
"structs": {
"flock": [
Expand Down
146 changes: 146 additions & 0 deletions tests/stat/test_fstatat.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*
* Copyright 2013 The Emscripten Authors. All rights reserved.
* Emscripten is available under two separate licenses, the MIT license and the
* University of Illinois/NCSA Open Source License. Both these licenses can be
* found in the LICENSE file.
*/

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>


void create_file(const char *path, const char *buffer, int mode) {
int fd = open(path, O_WRONLY | O_CREAT | O_EXCL, mode);
assert(fd >= 0);

int err = write(fd, buffer, sizeof(char) * strlen(buffer));
assert(err == (sizeof(char) * strlen(buffer)));

close(fd);
}

void setup() {
mkdir("folder", 0777);
create_file("folder/file", "abcdef", 0777);
symlink("file", "folder/file-link");
}

void test() {
int err;
struct stat s;

int fd = open(".", O_RDONLY);
assert(fd >= 0);

// missing file
err = fstatat(fd, "does_not_exist", &s, 0);
assert(err == -1);
printf("errno: %d\n", errno);
assert(errno == ENOENT);

// stat a folder
memset(&s, 0, sizeof(s));
err = fstatat(fd, "folder", &s, 0);
assert(!err);
assert(s.st_dev);
assert(s.st_ino);
assert(S_ISDIR(s.st_mode));
assert(s.st_nlink);
assert(s.st_rdev == 0);
assert(s.st_size);
assert(s.st_ctime);
#ifdef __EMSCRIPTEN__
assert(s.st_blksize == 4096);
assert(s.st_blocks == 1);
#endif

// stat a file
memset(&s, 0, sizeof(s));
err = fstatat(fd, "folder/file", &s, 0);
assert(!err);
assert(s.st_dev);
assert(s.st_ino);
assert(S_ISREG(s.st_mode));
assert(s.st_nlink);
assert(s.st_rdev == 0);
assert(s.st_size == 6);
assert(s.st_ctime);
#ifdef __EMSCRIPTEN__
assert(s.st_blksize == 4096);
assert(s.st_blocks == 1);
#endif

close(fd);

fd = open("folder", O_RDONLY);
assert(fd >= 0);

// missing file
err = fstatat(fd, "does_not_exist", &s, 0);
assert(err == -1);
assert(errno == ENOENT);

// stat a file
memset(&s, 0, sizeof(s));
err = fstatat(fd, "file", &s, 0);
assert(!err);

// empty path - not allowed
err = fstatat(fd, "", &s, 0);
assert(err == -1);
assert(errno == ENOENT);

// empty path - with AT_EMPTY_PATH (stat's the fd itself, a directory in this case)
memset(&s, 0, sizeof(s));
err = fstatat(fd, "", &s, AT_EMPTY_PATH);
assert(!err);
assert(s.st_dev);
assert(s.st_ino);
assert(S_ISDIR(s.st_mode));
assert(s.st_nlink);
assert(s.st_rdev == 0);
assert(s.st_size);
assert(s.st_ctime);
#ifdef __EMSCRIPTEN__
assert(s.st_blksize == 4096);
assert(s.st_blocks == 1);
#endif

close(fd);

fd = open("folder/file", O_RDONLY);
assert(fd >= 0);

// empty path - with AT_EMPTY_PATH (stat's the fd itself, a file)
memset(&s, 0, sizeof(s));
err = fstatat(fd, "", &s, AT_EMPTY_PATH);
assert(!err);
assert(s.st_dev);
assert(s.st_ino);
assert(S_ISREG(s.st_mode));
assert(s.st_nlink);
assert(s.st_rdev == 0);
assert(s.st_size == 6);
assert(s.st_ctime);
#ifdef __EMSCRIPTEN__
assert(s.st_blksize == 4096);
assert(s.st_blocks == 1);
#endif

close(fd);

puts("success");
}

int main() {
setup();
test();
return 0;
}
7 changes: 6 additions & 1 deletion tests/stat/test_stat.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ void create_file(const char *path, const char *buffer, int mode) {

void setup() {
struct utimbuf t = {1200000000, 1200000000};

mkdir("folder", 0777);
create_file("folder/file", "abcdef", 0777);
symlink("file", "folder/file-link");
Expand All @@ -53,6 +53,11 @@ void test() {
struct stat s;
struct utimbuf t = {1200000000, 1200000000};

// non-existent
err = stat("does_not_exist", &s);
assert(err == -1);
assert(errno == ENOENT);

// stat a folder
memset(&s, 0, sizeof(s));
err = stat("folder", &s);
Expand Down
3 changes: 3 additions & 0 deletions tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -4949,6 +4949,9 @@ def test_stat(self):
self.do_runf(path_from_root('tests', 'stat', 'test_stat.c'), 'success')
self.verify_in_strict_mode('test_stat.js')

def test_fstatat(self):
self.do_runf(path_from_root('tests', 'stat', 'test_fstatat.c'), 'success')

def test_stat_chmod(self):
self.do_runf(path_from_root('tests', 'stat', 'test_chmod.c'), 'success')

Expand Down
8 changes: 4 additions & 4 deletions tests/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -3762,15 +3762,15 @@ def test_stat_silly(self):
const char *path = argv[i];
struct stat path_stat;
if (stat(path, &path_stat) != 0) {
printf("Failed to stat path: %s; errno=%d\n", path, errno);
printf("Failed to stat path: '%s'; errno=%d\n", path, errno);
} else {
printf("ok on %s\n", path);
printf("stat success on '%s'\n", path);
}
}
}
''')
self.do_runf('src.cpp', r'''Failed to stat path: /a; errno=44
Failed to stat path: ; errno=44
self.do_runf('src.cpp', r'''Failed to stat path: '/a'; errno=44
Failed to stat path: ''; errno=44
''', args=['/a', ''])

def test_symlink_silly(self):
Expand Down

0 comments on commit 385a4b3

Please sign in to comment.