blob: 71a1d27e9fe37bd162f59f13a6639a7d52ec5283 [file] [log] [blame]
/*
* Copyright (C) Dmitry Volyntsev
* Copyright (C) NGINX, Inc.
*/
#include <njs_main.h>
#include <dirent.h>
#if (NJS_SOLARIS)
#define DT_DIR 0
#define DT_REG 1
#define DT_CHR 2
#define DT_LNK 3
#define DT_BLK 4
#define DT_FIFO 5
#define DT_SOCK 6
#define NJS_DT_INVALID 0xffffffff
#define njs_dentry_type(_dentry) \
(NJS_DT_INVALID)
#else
#define NJS_DT_INVALID 0xffffffff
#define njs_dentry_type(_dentry) \
((_dentry)->d_type)
#endif
#define njs_fs_magic(calltype, mode) \
(((mode) << 2) | calltype)
#define njs_fs_magic2(field, type) \
(((type) << 4) | field)
typedef enum {
NJS_FS_DIRECT,
NJS_FS_PROMISE,
NJS_FS_CALLBACK,
} njs_fs_calltype_t;
typedef enum {
NJS_FS_TRUNC,
NJS_FS_APPEND,
} njs_fs_writemode_t;
typedef enum {
NJS_FS_STAT,
NJS_FS_LSTAT,
} njs_fs_statmode_t;
typedef struct {
njs_str_t name;
int value;
} njs_fs_entry_t;
typedef enum {
NJS_FTW_PHYS = 1,
NJS_FTW_MOUNT = 2,
NJS_FTW_DEPTH = 8,
} njs_ftw_flags_t;
typedef enum {
NJS_FTW_F,
NJS_FTW_D,
NJS_FTW_DNR,
NJS_FTW_NS,
NJS_FTW_SL,
NJS_FTW_DP,
NJS_FTW_SLN,
} njs_ftw_type_t;
typedef struct {
long tv_sec;
long tv_nsec;
} njs_timespec_t;
typedef struct {
uint64_t st_dev;
uint64_t st_mode;
uint64_t st_nlink;
uint64_t st_uid;
uint64_t st_gid;
uint64_t st_rdev;
uint64_t st_ino;
uint64_t st_size;
uint64_t st_blksize;
uint64_t st_blocks;
njs_timespec_t st_atim;
njs_timespec_t st_mtim;
njs_timespec_t st_ctim;
njs_timespec_t st_birthtim;
} njs_stat_t;
typedef enum {
NJS_FS_STAT_DEV,
NJS_FS_STAT_INO,
NJS_FS_STAT_MODE,
NJS_FS_STAT_NLINK,
NJS_FS_STAT_UID,
NJS_FS_STAT_GID,
NJS_FS_STAT_RDEV,
NJS_FS_STAT_SIZE,
NJS_FS_STAT_BLKSIZE,
NJS_FS_STAT_BLOCKS,
NJS_FS_STAT_ATIME,
NJS_FS_STAT_BIRTHTIME,
NJS_FS_STAT_CTIME,
NJS_FS_STAT_MTIME,
} njs_stat_prop_t;
typedef njs_int_t (*njs_file_tree_walk_cb_t)(const char *, const struct stat *,
njs_ftw_type_t);
static njs_int_t njs_fs_access(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t calltype);
static njs_int_t njs_fs_mkdir(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t calltype);
static njs_int_t njs_fs_read(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t calltype);
static njs_int_t njs_fs_readdir(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t calltype);
static njs_int_t njs_fs_realpath(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t calltype);
static njs_int_t njs_fs_rename(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t calltype);
static njs_int_t njs_fs_rmdir(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t calltype);
static njs_int_t njs_fs_stat(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t calltype);
static njs_int_t njs_fs_symlink(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t calltype);
static njs_int_t njs_fs_unlink(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t calltype);
static njs_int_t njs_fs_write(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t calltype);
static njs_int_t njs_fs_constants(njs_vm_t *vm, njs_object_prop_t *prop,
njs_value_t *value, njs_value_t *unused, njs_value_t *retval);
static njs_int_t njs_fs_promises(njs_vm_t *vm, njs_object_prop_t *prop,
njs_value_t *value, njs_value_t *unused, njs_value_t *retval);
static njs_int_t njs_fs_dirent_constructor(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused);
static njs_int_t njs_fs_dirent_test(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t testtype);
static njs_int_t njs_fs_stats_test(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t testtype);
static njs_int_t njs_fs_stats_prop(njs_vm_t *vm, njs_object_prop_t *prop,
njs_value_t *value, njs_value_t *setval, njs_value_t *retval);
static njs_int_t njs_fs_stats_create(njs_vm_t *vm, struct stat *st,
njs_value_t *retval);
static njs_int_t njs_fs_fd_read(njs_vm_t *vm, int fd, njs_str_t *data);
static njs_int_t njs_fs_error(njs_vm_t *vm, const char *syscall,
const char *desc, const char *path, int errn, njs_value_t *retval);
static njs_int_t njs_fs_result(njs_vm_t *vm, njs_value_t *result,
njs_index_t calltype, const njs_value_t* callback, njs_uint_t nargs);
static njs_int_t njs_file_tree_walk(const char *path,
njs_file_tree_walk_cb_t cb, int fd_limit, njs_ftw_flags_t flags);
static njs_int_t njs_fs_make_path(njs_vm_t *vm, char *path, mode_t md,
njs_bool_t recursive, njs_value_t *retval);
static njs_int_t njs_fs_rmtree(njs_vm_t *vm, const char *path,
njs_bool_t recursive, njs_value_t *retval);
static const char *njs_fs_path(njs_vm_t *vm, char storage[NJS_MAX_PATH + 1],
const njs_value_t *src, const char *prop_name);
static int njs_fs_flags(njs_vm_t *vm, njs_value_t *value, int default_flags);
static mode_t njs_fs_mode(njs_vm_t *vm, njs_value_t *value,
mode_t default_mode);
static njs_int_t njs_fs_add_event(njs_vm_t *vm, const njs_value_t *callback,
const njs_value_t *args, njs_uint_t nargs);
static njs_int_t njs_fs_dirent_create(njs_vm_t *vm, njs_value_t *name,
njs_value_t *type, njs_value_t *retval);
static njs_int_t njs_fs_init(njs_vm_t *vm);
static const njs_value_t string_flag = njs_string("flag");
static const njs_value_t string_mode = njs_string("mode");
static const njs_value_t string_buffer = njs_string("buffer");
static const njs_value_t string_encoding = njs_string("encoding");
static const njs_value_t string_recursive = njs_string("recursive");
static njs_fs_entry_t njs_flags_table[] = {
{ njs_str("a"), O_APPEND | O_CREAT | O_WRONLY },
{ njs_str("a+"), O_APPEND | O_CREAT | O_RDWR },
{ njs_str("as"), O_APPEND | O_CREAT | O_SYNC | O_WRONLY },
{ njs_str("as+"), O_APPEND | O_CREAT | O_RDWR | O_SYNC },
{ njs_str("ax"), O_APPEND | O_CREAT | O_EXCL | O_WRONLY },
{ njs_str("ax+"), O_APPEND | O_CREAT | O_EXCL | O_RDWR },
{ njs_str("r"), O_RDONLY },
{ njs_str("r+"), O_RDWR },
{ njs_str("rs+"), O_RDWR | O_SYNC },
{ njs_str("w"), O_CREAT | O_TRUNC | O_WRONLY },
{ njs_str("w+"), O_CREAT | O_TRUNC | O_RDWR },
{ njs_str("wx"), O_CREAT | O_TRUNC | O_EXCL | O_WRONLY },
{ njs_str("wx+"), O_CREAT | O_TRUNC | O_EXCL | O_RDWR },
{ njs_null_str, 0 }
};
static njs_external_t njs_ext_fs[] = {
{
.flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL,
.name.symbol = NJS_SYMBOL_TO_STRING_TAG,
.u.property = {
.value = "fs",
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("access"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_access,
.magic8 = NJS_FS_CALLBACK,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("accessSync"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_access,
.magic8 = NJS_FS_DIRECT,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("appendFile"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_write,
.magic8 = njs_fs_magic(NJS_FS_CALLBACK, NJS_FS_APPEND),
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("appendFileSync"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_write,
.magic8 = njs_fs_magic(NJS_FS_DIRECT, NJS_FS_APPEND),
}
},
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("constants"),
.enumerable = 1,
.u.property = {
.handler = njs_fs_constants,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("Dirent"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_dirent_constructor,
.ctor = 1,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("lstat"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_stat,
.magic8 = njs_fs_magic(NJS_FS_CALLBACK, NJS_FS_LSTAT),
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("lstatSync"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_stat,
.magic8 = njs_fs_magic(NJS_FS_DIRECT, NJS_FS_LSTAT),
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("mkdir"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_mkdir,
.magic8 = NJS_FS_CALLBACK,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("mkdirSync"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_mkdir,
.magic8 = NJS_FS_DIRECT,
}
},
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("promises"),
.enumerable = 1,
.u.property = {
.handler = njs_fs_promises,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("readdir"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_readdir,
.magic8 = NJS_FS_CALLBACK,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("readdirSync"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_readdir,
.magic8 = NJS_FS_DIRECT,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("readFile"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_read,
.magic8 = NJS_FS_CALLBACK,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("readFileSync"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_read,
.magic8 = NJS_FS_DIRECT,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("realpath"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_realpath,
.magic8 = NJS_FS_CALLBACK,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("realpathSync"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_realpath,
.magic8 = NJS_FS_DIRECT,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("rename"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_rename,
.magic8 = NJS_FS_CALLBACK,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("renameSync"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_rename,
.magic8 = NJS_FS_DIRECT,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("rmdir"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_rmdir,
.magic8 = NJS_FS_CALLBACK,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("rmdirSync"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_rmdir,
.magic8 = NJS_FS_DIRECT,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("stat"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_stat,
.magic8 = njs_fs_magic(NJS_FS_CALLBACK, NJS_FS_STAT),
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("statSync"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_stat,
.magic8 = njs_fs_magic(NJS_FS_DIRECT, NJS_FS_STAT),
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("symlink"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_symlink,
.magic8 = NJS_FS_CALLBACK,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("symlinkSync"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_symlink,
.magic8 = NJS_FS_DIRECT,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("unlink"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_unlink,
.magic8 = NJS_FS_CALLBACK,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("unlinkSync"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_unlink,
.magic8 = NJS_FS_DIRECT,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("writeFile"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_write,
.magic8 = njs_fs_magic(NJS_FS_CALLBACK, NJS_FS_TRUNC),
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("writeFileSync"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_write,
.magic8 = njs_fs_magic(NJS_FS_DIRECT, NJS_FS_TRUNC),
}
},
};
static njs_external_t njs_ext_dirent[] = {
{
.flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL,
.name.symbol = NJS_SYMBOL_TO_STRING_TAG,
.u.property = {
.value = "Dirent",
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("constructor"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_dirent_constructor,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("isBlockDevice"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_dirent_test,
.magic8 = DT_BLK,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("isCharacterDevice"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_dirent_test,
.magic8 = DT_CHR,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("isDirectory"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_dirent_test,
.magic8 = DT_DIR,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("isFIFO"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_dirent_test,
.magic8 = DT_FIFO,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("isFile"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_dirent_test,
.magic8 = DT_REG,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("isSocket"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_dirent_test,
.magic8 = DT_SOCK,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("isSymbolicLink"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_dirent_test,
.magic8 = DT_LNK,
}
},
};
static njs_external_t njs_ext_stats[] = {
{
.flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL,
.name.symbol = NJS_SYMBOL_TO_STRING_TAG,
.u.property = {
.value = "Stats",
}
},
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("atime"),
.enumerable = 1,
.u.property = {
.handler = njs_fs_stats_prop,
.magic32 = njs_fs_magic2(NJS_FS_STAT_ATIME, NJS_DATE),
}
},
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("atimeMs"),
.enumerable = 1,
.u.property = {
.handler = njs_fs_stats_prop,
.magic32 = njs_fs_magic2(NJS_FS_STAT_ATIME, NJS_NUMBER),
}
},
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("birthtime"),
.enumerable = 1,
.u.property = {
.handler = njs_fs_stats_prop,
.magic32 = njs_fs_magic2(NJS_FS_STAT_BIRTHTIME, NJS_DATE),
}
},
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("birthtimeMs"),
.enumerable = 1,
.u.property = {
.handler = njs_fs_stats_prop,
.magic32 = njs_fs_magic2(NJS_FS_STAT_BIRTHTIME, NJS_NUMBER),
}
},
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("ctime"),
.enumerable = 1,
.u.property = {
.handler = njs_fs_stats_prop,
.magic32 = njs_fs_magic2(NJS_FS_STAT_CTIME, NJS_DATE),
}
},
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("ctimeMs"),
.enumerable = 1,
.u.property = {
.handler = njs_fs_stats_prop,
.magic32 = njs_fs_magic2(NJS_FS_STAT_CTIME, NJS_NUMBER),
}
},
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("blksize"),
.enumerable = 1,
.u.property = {
.handler = njs_fs_stats_prop,
.magic32 = njs_fs_magic2(NJS_FS_STAT_BLKSIZE, NJS_NUMBER),
}
},
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("blocks"),
.enumerable = 1,
.u.property = {
.handler = njs_fs_stats_prop,
.magic32 = njs_fs_magic2(NJS_FS_STAT_BLOCKS, NJS_NUMBER),
}
},
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("dev"),
.enumerable = 1,
.u.property = {
.handler = njs_fs_stats_prop,
.magic32 = njs_fs_magic2(NJS_FS_STAT_DEV, NJS_NUMBER),
}
},
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("gid"),
.enumerable = 1,
.u.property = {
.handler = njs_fs_stats_prop,
.magic32 = njs_fs_magic2(NJS_FS_STAT_GID, NJS_NUMBER),
}
},
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("ino"),
.enumerable = 1,
.u.property = {
.handler = njs_fs_stats_prop,
.magic32 = njs_fs_magic2(NJS_FS_STAT_INO, NJS_NUMBER),
}
},
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("mode"),
.enumerable = 1,
.u.property = {
.handler = njs_fs_stats_prop,
.magic32 = njs_fs_magic2(NJS_FS_STAT_MODE, NJS_NUMBER),
}
},
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("mtime"),
.enumerable = 1,
.u.property = {
.handler = njs_fs_stats_prop,
.magic32 = njs_fs_magic2(NJS_FS_STAT_MTIME, NJS_DATE),
}
},
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("mtimeMs"),
.enumerable = 1,
.u.property = {
.handler = njs_fs_stats_prop,
.magic32 = njs_fs_magic2(NJS_FS_STAT_MTIME, NJS_NUMBER),
}
},
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("nlink"),
.enumerable = 1,
.u.property = {
.handler = njs_fs_stats_prop,
.magic32 = njs_fs_magic2(NJS_FS_STAT_NLINK, NJS_NUMBER),
}
},
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("rdev"),
.enumerable = 1,
.u.property = {
.handler = njs_fs_stats_prop,
.magic32 = njs_fs_magic2(NJS_FS_STAT_RDEV, NJS_NUMBER),
}
},
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("size"),
.enumerable = 1,
.u.property = {
.handler = njs_fs_stats_prop,
.magic32 = njs_fs_magic2(NJS_FS_STAT_SIZE, NJS_NUMBER),
}
},
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("uid"),
.enumerable = 1,
.u.property = {
.handler = njs_fs_stats_prop,
.magic32 = njs_fs_magic2(NJS_FS_STAT_UID, NJS_NUMBER),
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("isBlockDevice"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_stats_test,
.magic8 = DT_BLK,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("isCharacterDevice"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_stats_test,
.magic8 = DT_CHR,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("isDirectory"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_stats_test,
.magic8 = DT_DIR,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("isFIFO"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_stats_test,
.magic8 = DT_FIFO,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("isFile"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_stats_test,
.magic8 = DT_REG,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("isSocket"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_stats_test,
.magic8 = DT_SOCK,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("isSymbolicLink"),
.writable = 1,
.configurable = 1,
.u.method = {
.native = njs_fs_stats_test,
.magic8 = DT_LNK,
}
},
};
static njs_int_t njs_fs_stats_proto_id;
static njs_int_t njs_fs_dirent_proto_id;
njs_module_t njs_fs_module = {
.name = njs_str("fs"),
.init = njs_fs_init,
};
static njs_int_t
njs_fs_access(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t calltype)
{
int md;
njs_int_t ret;
const char *path;
njs_value_t retval, *callback, *mode;
char path_buf[NJS_MAX_PATH + 1];
path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path");
if (njs_slow_path(path == NULL)) {
return NJS_ERROR;
}
callback = NULL;
mode = njs_arg(args, nargs, 2);
if (calltype == NJS_FS_CALLBACK) {
callback = njs_arg(args, nargs, njs_min(nargs - 1, 3));
if (!njs_is_function(callback)) {
njs_type_error(vm, "\"callback\" must be a function");
return NJS_ERROR;
}
if (mode == callback) {
mode = njs_value_arg(&njs_value_undefined);
}
}
switch (mode->type) {
case NJS_UNDEFINED:
md = F_OK;
break;
case NJS_NUMBER:
md = njs_number(mode);
break;
default:
njs_type_error(vm, "\"mode\" must be a number");
return NJS_ERROR;
}
njs_set_undefined(&retval);
ret = access(path, md);
if (njs_slow_path(ret != 0)) {
ret = njs_fs_error(vm, "access", strerror(errno), path, errno, &retval);
}
if (ret == NJS_OK) {
return njs_fs_result(vm, &retval, calltype, callback, 1);
}
return NJS_ERROR;
}
static njs_int_t
njs_fs_mkdir(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t calltype)
{
char *path;
mode_t md;
njs_int_t ret;
njs_value_t mode, recursive, retval, *callback, *options;
char path_buf[NJS_MAX_PATH + 1];
path = (char *) njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path");
if (njs_slow_path(path == NULL)) {
return NJS_ERROR;
}
callback = NULL;
options = njs_arg(args, nargs, 2);
if (njs_slow_path(calltype == NJS_FS_CALLBACK)) {
callback = njs_arg(args, nargs, njs_min(nargs - 1, 3));
if (!njs_is_function(callback)) {
njs_type_error(vm, "\"callback\" must be a function");
return NJS_ERROR;
}
if (options == callback) {
options = njs_value_arg(&njs_value_undefined);
}
}
njs_set_undefined(&mode);
njs_set_false(&recursive);
switch (options->type) {
case NJS_NUMBER:
mode = *options;
break;
case NJS_UNDEFINED:
break;
default:
if (!njs_is_object(options)) {
njs_type_error(vm, "Unknown options type: \"%s\" "
"(a number or object required)",
njs_type_string(options->type));
return NJS_ERROR;
}
ret = njs_value_property(vm, options, njs_value_arg(&string_mode),
&mode);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
ret = njs_value_property(vm, options, njs_value_arg(&string_recursive),
&recursive);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
}
md = njs_fs_mode(vm, &mode, 0777);
if (njs_slow_path(md == (mode_t) -1)) {
return NJS_ERROR;
}
ret = njs_fs_make_path(vm, path, md, njs_is_true(&recursive), &retval);
if (ret == NJS_OK) {
return njs_fs_result(vm, &retval, calltype, callback, 1);
}
return NJS_ERROR;
}
static njs_int_t
njs_fs_read(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t calltype)
{
int fd, flags;
njs_str_t data;
njs_int_t ret;
const char *path;
njs_value_t flag, encode, retval, *callback, *options;
struct stat sb;
const njs_buffer_encoding_t *encoding;
char path_buf[NJS_MAX_PATH + 1];
path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path");
if (njs_slow_path(path == NULL)) {
return NJS_ERROR;
}
callback = NULL;
options = njs_arg(args, nargs, 2);
if (calltype == NJS_FS_CALLBACK) {
callback = njs_arg(args, nargs, njs_min(nargs - 1, 3));
if (!njs_is_function(callback)) {
njs_type_error(vm, "\"callback\" must be a function");
return NJS_ERROR;
}
if (options == callback) {
options = njs_value_arg(&njs_value_undefined);
}
}
njs_set_undefined(&flag);
njs_set_undefined(&encode);
switch (options->type) {
case NJS_STRING:
encode = *options;
break;
case NJS_UNDEFINED:
break;
default:
if (!njs_is_object(options)) {
njs_type_error(vm, "Unknown options type: \"%s\" "
"(a string or object required)",
njs_type_string(options->type));
return NJS_ERROR;
}
ret = njs_value_property(vm, options, njs_value_arg(&string_flag),
&flag);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
ret = njs_value_property(vm, options, njs_value_arg(&string_encoding),
&encode);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
}
flags = njs_fs_flags(vm, &flag, O_RDONLY);
if (njs_slow_path(flags == -1)) {
return NJS_ERROR;
}
encoding = NULL;
if (njs_is_defined(&encode)) {
encoding = njs_buffer_encoding(vm, &encode);
if (njs_slow_path(encoding == NULL)) {
return NJS_ERROR;
}
}
fd = open(path, flags);
if (njs_slow_path(fd < 0)) {
ret = njs_fs_error(vm, "open", strerror(errno), path, errno, &retval);
goto done;
}
ret = fstat(fd, &sb);
if (njs_slow_path(ret == -1)) {
ret = njs_fs_error(vm, "stat", strerror(errno), path, errno, &retval);
goto done;
}
if (njs_slow_path(!S_ISREG(sb.st_mode))) {
ret = njs_fs_error(vm, "stat", "File is not regular", path, 0, &retval);
goto done;
}
data.start = NULL;
data.length = sb.st_size;
ret = njs_fs_fd_read(vm, fd, &data);
if (njs_slow_path(ret != NJS_OK)) {
if (ret == NJS_DECLINED) {
ret = njs_fs_error(vm, "read", strerror(errno), path, errno,
&retval);
}
goto done;
}
if (encoding == NULL) {
ret = njs_buffer_set(vm, &retval, data.start, data.length);
} else {
ret = encoding->encode(vm, &retval, &data);
njs_mp_free(vm->mem_pool, data.start);
}
done:
if (fd != -1) {
(void) close(fd);
}
if (ret == NJS_OK) {
return njs_fs_result(vm, &retval, calltype, callback, 2);
}
return NJS_ERROR;
}
static njs_int_t
njs_fs_readdir(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t calltype)
{
DIR *dir;
njs_str_t s;
njs_int_t ret;
const char *path;
njs_value_t encode, types, ename, etype, retval,
*callback, *options, *value;
njs_array_t *results;
struct dirent *entry;
const njs_buffer_encoding_t *encoding;
char path_buf[NJS_MAX_PATH + 1];
static const njs_value_t string_types = njs_string("withFileTypes");
path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path");
if (njs_slow_path(path == NULL)) {
return NJS_ERROR;
}
callback = NULL;
options = njs_arg(args, nargs, 2);
if (njs_slow_path(calltype == NJS_FS_CALLBACK)) {
callback = njs_arg(args, nargs, njs_min(nargs - 1, 3));
if (!njs_is_function(callback)) {
njs_type_error(vm, "\"callback\" must be a function");
return NJS_ERROR;
}
if (options == callback) {
options = njs_value_arg(&njs_value_undefined);
}
}
njs_set_false(&types);
njs_set_undefined(&encode);
switch (options->type) {
case NJS_STRING:
encode = *options;
break;
case NJS_UNDEFINED:
break;
default:
if (!njs_is_object(options)) {
njs_type_error(vm, "Unknown options type: \"%s\" "
"(a string or object required)",
njs_type_string(options->type));
return NJS_ERROR;
}
ret = njs_value_property(vm, options, njs_value_arg(&string_encoding),
&encode);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
ret = njs_value_property(vm, options, njs_value_arg(&string_types),
&types);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
}
encoding = NULL;
if (!njs_is_string(&encode) || !njs_string_eq(&encode, &string_buffer)) {
encoding = njs_buffer_encoding(vm, &encode);
if (njs_slow_path(encoding == NULL)) {
return NJS_ERROR;
}
}
results = njs_array_alloc(vm, 1, 0, NJS_ARRAY_SPARE);
if (njs_slow_path(results == NULL)) {
return NJS_ERROR;
}
njs_set_array(&retval, results);
dir = opendir(path);
if (njs_slow_path(dir == NULL)) {
ret = njs_fs_error(vm, "opendir", strerror(errno), path, errno,
&retval);
goto done;
}
ret = NJS_OK;
for ( ;; ) {
errno = 0;
entry = readdir(dir);
if (njs_slow_path(entry == NULL)) {
if (errno != 0) {
ret = njs_fs_error(vm, "readdir", strerror(errno), path, errno,
&retval);
}
goto done;
}
s.start = (u_char *) entry->d_name;
s.length = njs_strlen(s.start);
if ((s.length == 1 && s.start[0] == '.')
|| (s.length == 2 && (s.start[0] == '.' && s.start[1] == '.')))
{
continue;
}
value = njs_array_push(vm, results);
if (njs_slow_path(value == NULL)) {
goto done;
}
if (encoding == NULL) {
ret = njs_buffer_set(vm, &ename, s.start, s.length);
} else {
ret = encoding->encode(vm, &ename, &s);
}
if (njs_slow_path(ret != NJS_OK)) {
goto done;
}
if (njs_fast_path(!njs_is_true(&types))) {
*value = ename;
continue;
}
njs_set_number(&etype, njs_dentry_type(entry));
ret = njs_fs_dirent_create(vm, &ename, &etype, value);
if (njs_slow_path(ret != NJS_OK)) {
goto done;
}
}
done:
if (dir != NULL) {
(void) closedir(dir);
}
if (ret == NJS_OK) {
return njs_fs_result(vm, &retval, calltype, callback, 2);
}
return NJS_ERROR;
}
static njs_int_t
njs_fs_realpath(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t calltype)
{
njs_int_t ret;
njs_str_t s;
const char *path;
njs_value_t encode, retval, *callback, *options;
const njs_buffer_encoding_t *encoding;
char path_buf[NJS_MAX_PATH + 1],
dst_buf[NJS_MAX_PATH + 1];
path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path");
if (njs_slow_path(path == NULL)) {
return NJS_ERROR;
}
callback = NULL;
options = njs_arg(args, nargs, 2);
if (calltype == NJS_FS_CALLBACK) {
callback = njs_arg(args, nargs, njs_min(nargs - 1, 3));
if (!njs_is_function(callback)) {
njs_type_error(vm, "\"callback\" must be a function");
return NJS_ERROR;
}
if (options == callback) {
options = njs_value_arg(&njs_value_undefined);
}
}
njs_set_undefined(&encode);
switch (options->type) {
case NJS_STRING:
encode = *options;
break;
case NJS_UNDEFINED:
break;
default:
if (!njs_is_object(options)) {
njs_type_error(vm, "Unknown options type: \"%s\" "
"(a string or object required)",
njs_type_string(options->type));
return NJS_ERROR;
}
ret = njs_value_property(vm, options, njs_value_arg(&string_encoding),
&encode);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
}
encoding = NULL;
if (!njs_is_string(&encode) || !njs_string_eq(&encode, &string_buffer)) {
encoding = njs_buffer_encoding(vm, &encode);
if (njs_slow_path(encoding == NULL)) {
return NJS_ERROR;
}
}
s.start = (u_char *) realpath(path, dst_buf);
if (njs_slow_path(s.start == NULL)) {
ret = njs_fs_error(vm, "realpath", strerror(errno), path, errno,
&retval);
goto done;
}
s.length = njs_strlen(s.start);
if (encoding == NULL) {
ret = njs_buffer_new(vm, &retval, s.start, s.length);
} else {
ret = encoding->encode(vm, &retval, &s);
}
done:
if (ret == NJS_OK) {
return njs_fs_result(vm, &retval, calltype, callback, 2);
}
return NJS_ERROR;
}
static njs_int_t
njs_fs_rename(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t calltype)
{
njs_int_t ret;
const char *path, *newpath;
njs_value_t retval, *callback;
char path_buf[NJS_MAX_PATH + 1], newpath_buf[NJS_MAX_PATH + 1];
callback = NULL;
if (calltype == NJS_FS_CALLBACK) {
callback = njs_arg(args, nargs, 3);
if (!njs_is_function(callback)) {
njs_type_error(vm, "\"callback\" must be a function");
return NJS_ERROR;
}
}
path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "oldPath");
if (njs_slow_path(path == NULL)) {
return NJS_ERROR;
}
newpath = njs_fs_path(vm, newpath_buf, njs_arg(args, nargs, 2), "newPath");
if (njs_slow_path(newpath == NULL)) {
return NJS_ERROR;
}
njs_set_undefined(&retval);
ret = rename(path, newpath);
if (njs_slow_path(ret != 0)) {
ret = njs_fs_error(vm, "rename", strerror(errno), NULL, errno, &retval);
}
if (ret == NJS_OK) {
return njs_fs_result(vm, &retval, calltype, callback, 1);
}
return NJS_ERROR;
}
static njs_int_t
njs_fs_rmdir(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t calltype)
{
njs_int_t ret;
const char *path;
njs_value_t recursive, retval, *callback, *options;
char path_buf[NJS_MAX_PATH + 1];
path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path");
if (njs_slow_path(path == NULL)) {
return NJS_ERROR;
}
callback = NULL;
options = njs_arg(args, nargs, 2);
if (njs_slow_path(calltype == NJS_FS_CALLBACK)) {
callback = njs_arg(args, nargs, njs_min(nargs - 1, 3));
if (!njs_is_function(callback)) {
njs_type_error(vm, "\"callback\" must be a function");
return NJS_ERROR;
}
if (options == callback) {
options = njs_value_arg(&njs_value_undefined);
}
}
njs_set_false(&recursive);
switch (options->type) {
case NJS_UNDEFINED:
break;
default:
if (!njs_is_object(options)) {
njs_type_error(vm, "Unknown options type: \"%s\" "
"(an object required)",
njs_type_string(options->type));
return NJS_ERROR;
}
ret = njs_value_property(vm, options, njs_value_arg(&string_recursive),
&recursive);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
}
ret = njs_fs_rmtree(vm, path, njs_is_true(&recursive), &retval);
if (ret == NJS_OK) {
return njs_fs_result(vm, &retval, calltype, callback, 1);
}
return NJS_ERROR;
}
static njs_int_t
njs_fs_stat(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t magic)
{
njs_int_t ret;
njs_bool_t throw;
struct stat sb;
const char *path;
njs_value_t retval, *callback, *options;
njs_fs_calltype_t calltype;
char path_buf[NJS_MAX_PATH + 1];
static const njs_value_t string_bigint = njs_string("bigint");
static const njs_value_t string_throw = njs_string("throwIfNoEntry");
path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path");
if (njs_slow_path(path == NULL)) {
return NJS_ERROR;
}
callback = NULL;
calltype = magic & 3;
options = njs_arg(args, nargs, 2);
if (njs_slow_path(calltype == NJS_FS_CALLBACK)) {
callback = njs_arg(args, nargs, njs_min(nargs - 1, 3));
if (!njs_is_function(callback)) {
njs_type_error(vm, "\"callback\" must be a function");
return NJS_ERROR;
}
if (options == callback) {
options = njs_value_arg(&njs_value_undefined);
}
}
throw = 1;
switch (options->type) {
case NJS_UNDEFINED:
break;
default:
if (!njs_is_object(options)) {
njs_type_error(vm, "Unknown options type: \"%s\" "
"(an object required)",
njs_type_string(options->type));
return NJS_ERROR;
}
ret = njs_value_property(vm, options, njs_value_arg(&string_bigint),
&retval);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
if (njs_bool(&retval)) {
njs_type_error(vm, "\"bigint\" is not supported");
return NJS_ERROR;
}
if (calltype == NJS_FS_DIRECT) {
ret = njs_value_property(vm, options, njs_value_arg(&string_throw),
&retval);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
throw = njs_bool(&retval);
}
}
ret = ((magic >> 2) == NJS_FS_STAT) ? stat(path, &sb) : lstat(path, &sb);
if (njs_slow_path(ret != 0)) {
if (errno != ENOENT || throw) {
ret = njs_fs_error(vm,
((magic >> 2) == NJS_FS_STAT) ? "stat" : "lstat",
strerror(errno), path, errno, &retval);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
} else {
njs_set_undefined(&retval);
}
return njs_fs_result(vm, &retval, calltype, callback, 2);
}
ret = njs_fs_stats_create(vm, &sb, &retval);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
return njs_fs_result(vm, &retval, calltype, callback, 2);
}
static njs_int_t
njs_fs_symlink(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t calltype)
{
njs_int_t ret;
const char *target, *path;
njs_value_t retval, *callback, *type;
char target_buf[NJS_MAX_PATH + 1], path_buf[NJS_MAX_PATH + 1];
target = njs_fs_path(vm, target_buf, njs_arg(args, nargs, 1), "target");
if (njs_slow_path(target == NULL)) {
return NJS_ERROR;
}
path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 2), "path");
if (njs_slow_path(path == NULL)) {
return NJS_ERROR;
}
callback = NULL;
type = njs_arg(args, nargs, 3);
if (calltype == NJS_FS_CALLBACK) {
callback = njs_arg(args, nargs, njs_min(nargs - 1, 4));
if (!njs_is_function(callback)) {
njs_type_error(vm, "\"callback\" must be a function");
return NJS_ERROR;
}
if (type == callback) {
type = njs_value_arg(&njs_value_undefined);
}
}
if (njs_slow_path(!njs_is_undefined(type) && !njs_is_string(type))) {
njs_type_error(vm, "\"type\" must be a string");
return NJS_ERROR;
}
njs_set_undefined(&retval);
ret = symlink(target, path);
if (njs_slow_path(ret != 0)) {
ret = njs_fs_error(vm, "symlink", strerror(errno), path, errno,
&retval);
}
if (ret == NJS_OK) {
return njs_fs_result(vm, &retval, calltype, callback, 1);
}
return NJS_ERROR;
}
static njs_int_t
njs_fs_unlink(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t calltype)
{
njs_int_t ret;
const char *path;
njs_value_t retval, *callback;
char path_buf[NJS_MAX_PATH + 1];
path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path");
if (njs_slow_path(path == NULL)) {
return NJS_ERROR;
}
callback = NULL;
if (calltype == NJS_FS_CALLBACK) {
callback = njs_arg(args, nargs, 2);
if (!njs_is_function(callback)) {
njs_type_error(vm, "\"callback\" must be a function");
return NJS_ERROR;
}
}
njs_set_undefined(&retval);
ret = unlink(path);
if (njs_slow_path(ret != 0)) {
ret = njs_fs_error(vm, "unlink", strerror(errno), path, errno, &retval);
}
if (ret == NJS_OK) {
return njs_fs_result(vm, &retval, calltype, callback, 1);
}
return NJS_ERROR;
}
static njs_int_t
njs_fs_write(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t magic)
{
int fd, flags;
u_char *p, *end;
mode_t md;
ssize_t n;
njs_str_t content;
njs_int_t ret;
const char *path;
njs_value_t flag, mode, encode, retval, *data, *callback,
*options;
njs_typed_array_t *array;
njs_fs_calltype_t calltype;
njs_array_buffer_t *buffer;
const njs_buffer_encoding_t *encoding;
char path_buf[NJS_MAX_PATH + 1];
path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path");
if (njs_slow_path(path == NULL)) {
return NJS_ERROR;
}
callback = NULL;
calltype = magic & 3;
options = njs_arg(args, nargs, 3);
if (calltype == NJS_FS_CALLBACK) {
callback = njs_arg(args, nargs, njs_min(nargs - 1, 4));
if (!njs_is_function(callback)) {
njs_type_error(vm, "\"callback\" must be a function");
return NJS_ERROR;
}
if (options == callback) {
options = njs_value_arg(&njs_value_undefined);
}
}
njs_set_undefined(&flag);
njs_set_undefined(&mode);
njs_set_undefined(&encode);
switch (options->type) {
case NJS_STRING:
encode = *options;
break;
case NJS_UNDEFINED:
break;
default:
if (!njs_is_object(options)) {
njs_type_error(vm, "Unknown options type: \"%s\" "
"(a string or object required)",
njs_type_string(options->type));
return NJS_ERROR;
}
ret = njs_value_property(vm, options, njs_value_arg(&string_flag),
&flag);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
ret = njs_value_property(vm, options, njs_value_arg(&string_mode),
&mode);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
ret = njs_value_property(vm, options, njs_value_arg(&string_encoding),
&encode);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
}
data = njs_arg(args, nargs, 2);
switch (data->type) {
case NJS_TYPED_ARRAY:
case NJS_DATA_VIEW:
array = njs_typed_array(data);
buffer = array->buffer;
if (njs_slow_path(njs_is_detached_buffer(buffer))) {
njs_type_error(vm, "detached buffer");
return NJS_ERROR;
}
content.start = &buffer->u.u8[array->offset];
content.length = array->byte_length;
break;
case NJS_STRING:
default:
encoding = njs_buffer_encoding(vm, &encode);
if (njs_slow_path(encoding == NULL)) {
return NJS_ERROR;
}
ret = njs_value_to_string(vm, &retval, data);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
ret = njs_buffer_decode_string(vm, &retval, &retval, encoding);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
njs_string_get(&retval, &content);
break;
}
flags = njs_fs_flags(vm, &flag, O_CREAT | O_WRONLY);
if (njs_slow_path(flags == -1)) {
return NJS_ERROR;
}
flags |= ((magic >> 2) == NJS_FS_APPEND) ? O_APPEND : O_TRUNC;
md = njs_fs_mode(vm, &mode, 0666);
if (njs_slow_path(md == (mode_t) -1)) {
return NJS_ERROR;
}
fd = open(path, flags, md);
if (njs_slow_path(fd < 0)) {
ret = njs_fs_error(vm, "open", strerror(errno), path, errno, &retval);
goto done;
}
p = content.start;
end = p + content.length;
while (p < end) {
n = write(fd, p, end - p);
if (njs_slow_path(n == -1)) {
if (errno == EINTR) {
continue;
}
ret = njs_fs_error(vm, "write", strerror(errno), path, errno,
&retval);
goto done;
}
p += n;
}
ret = NJS_OK;
njs_set_undefined(&retval);
done:
if (fd != -1) {
(void) close(fd);
}
if (ret == NJS_OK) {
return njs_fs_result(vm, &retval, calltype, callback, 1);
}
return NJS_ERROR;
}
static njs_int_t
njs_fs_fd_read(njs_vm_t *vm, int fd, njs_str_t *data)
{
u_char *p, *end, *start;
size_t size;
ssize_t n;
size = data->length;
if (size == 0) {
size = 4096;
}
data->start = njs_mp_alloc(vm->mem_pool, size);
if (data->start == NULL) {
njs_memory_error(vm);
return NJS_ERROR;
}
p = data->start;
end = p + size;
for ( ;; ) {
n = read(fd, p, end - p);
if (njs_slow_path(n < 0)) {
return NJS_DECLINED;
}
p += n;
if (n == 0) {
break;
}
if (end - p < 2048) {
size *= 2;
start = njs_mp_alloc(vm->mem_pool, size);
if (start == NULL) {
njs_memory_error(vm);
return NJS_ERROR;
}
memcpy(start, data->start, p - data->start);
njs_mp_free(vm->mem_pool, data->start);
p = start + (p - data->start);
end = start + size;
data->start = start;
}
}
data->length = p - data->start;
return NJS_OK;
}
static njs_int_t
njs_fs_make_path(njs_vm_t *vm, char *path, mode_t md, njs_bool_t recursive,
njs_value_t *retval)
{
int err;
njs_int_t ret;
const char *p, *prev, *end;
struct stat sb;
njs_set_undefined(retval);
end = path + njs_strlen(path);
if (!recursive) {
ret = mkdir(path, md);
if (ret != 0) {
err = errno;
goto failed;
}
return NJS_OK;
}
p = path;
prev = p;
for ( ;; ) {
p = strchr(prev + 1, '/');
if (p == NULL) {
p = end;
}
if (njs_slow_path((p - path) > NJS_MAX_PATH)) {
njs_internal_error(vm, "too large path");
return NJS_ERROR;
}
path[p - path] = '\0';
ret = mkdir(path, md);
err = errno;
switch (ret) {
case 0:
break;
case EACCES:
case ENOTDIR:
case EPERM:
goto failed;
case EEXIST:
default:
ret = stat(path, &sb);
if (ret == 0) {
if (!S_ISDIR(sb.st_mode)) {
err = ENOTDIR;
goto failed;
}
break;
}
goto failed;
}
if (p == end) {
break;
}
path[p - path] = '/';
prev = p;
}
return NJS_OK;
failed:
return njs_fs_error(vm, "mkdir", strerror(err), path, err, retval);
}
typedef struct njs_ftw_trace_s njs_ftw_trace_t;
struct njs_ftw_trace_s {
struct njs_ftw_trace_s *chain;
dev_t dev;
ino_t ino;
};
static int
njs_ftw(char *path, njs_file_tree_walk_cb_t cb, int fd_limit,
njs_ftw_flags_t flags, njs_ftw_trace_t *parent)
{
int type, ret, dfd;
DIR *d;
size_t base, len, length;
const char *d_name;
struct stat st;
struct dirent *entry;
njs_ftw_trace_t trace, *h;
ret = (flags & NJS_FTW_PHYS) ? lstat(path, &st) : stat(path, &st);
if (ret < 0) {
if (!(flags & NJS_FTW_PHYS) && errno == ENOENT && !lstat(path, &st)) {
type = NJS_FTW_SLN;
} else if (errno != EACCES) {
return NJS_ERROR;
} else {
type = NJS_FTW_NS;
}
} else if (S_ISDIR(st.st_mode)) {
type = (flags & NJS_FTW_DEPTH) ? NJS_FTW_DP : NJS_FTW_D;
} else if (S_ISLNK(st.st_mode)) {
type = (flags & NJS_FTW_PHYS) ? NJS_FTW_SL : NJS_FTW_SLN;
} else {
type = NJS_FTW_F;
}
if ((flags & NJS_FTW_MOUNT) && parent != NULL && st.st_dev != parent->dev) {
return NJS_OK;
}
for (h = parent; h != NULL; h = h->chain) {
if (h->dev == st.st_dev && h->ino == st.st_ino) {
return NJS_OK;
}
}
len = njs_strlen(path);
base = len && (path[len - 1] == '/') ? len - 1 : len;
trace.chain = parent;
trace.dev = st.st_dev;
trace.ino = st.st_ino;
d = NULL;
dfd = -1;
if (type == NJS_FTW_D || type == NJS_FTW_DP) {
dfd = open(path, O_RDONLY);
if (dfd < 0) {
if (errno != EACCES) {
return NJS_ERROR;
}
type = NJS_FTW_DNR;
}
}
if (!(flags & NJS_FTW_DEPTH)) {
ret = cb(path, &st, type);
if (njs_slow_path(ret != 0)) {
goto done;
}
}
if (type == NJS_FTW_D || type == NJS_FTW_DP) {
d = fdopendir(dfd);
if (njs_slow_path(d == NULL)) {
ret = NJS_ERROR;
goto done;
}
for ( ;; ) {
entry = readdir(d);
if (entry == NULL) {
break;
}
d_name = entry->d_name;
length = njs_strlen(d_name);
if ((length == 1 && d_name[0] == '.')
|| (length == 2 && (d_name[0] == '.' && d_name[1] == '.')))
{
continue;
}
if (njs_slow_path(length >= (NJS_MAX_PATH - len))) {
errno = ENAMETOOLONG;
ret = NJS_ERROR;
goto done;
}
path[base] = '/';
memcpy(&path[base + 1], d_name, length + njs_length("\0"));
if (fd_limit != 0) {
ret = njs_ftw(path, cb, fd_limit - 1, flags, &trace);
if (njs_slow_path(ret != 0)) {
goto done;
}
}
}
(void) closedir(d);
d = NULL;
dfd = -1;
}
path[len] = '\0';
if (flags & NJS_FTW_DEPTH) {
ret = cb(path, &st, type);
if (njs_slow_path(ret != 0)) {
return ret;
}
}
ret = NJS_OK;
done:
if (d != NULL) {
/* closedir() also closes underlying dfd. */
(void) closedir(d);
} else if (dfd >= 0) {
(void) close(dfd);
}
return ret;
}
static njs_int_t
njs_file_tree_walk(const char *path, njs_file_tree_walk_cb_t cb, int fd_limit,
njs_ftw_flags_t flags)
{
size_t len;
char pathbuf[NJS_MAX_PATH + 1];
len = njs_strlen(path);
if (njs_slow_path(len > NJS_MAX_PATH)) {
errno = ENAMETOOLONG;
return -1;
}
memcpy(pathbuf, path, len + 1);
return njs_ftw(pathbuf, cb, fd_limit, flags, NULL);
}
static njs_int_t
njs_fs_rmtree_cb(const char *path, const struct stat *sb, njs_ftw_type_t type)
{
njs_int_t ret;
ret = remove(path);
if (ret != 0) {
return NJS_ERROR;
}
return NJS_OK;
}
static njs_int_t
njs_fs_rmtree(njs_vm_t *vm, const char *path, njs_bool_t recursive,
njs_value_t *retval)
{
njs_int_t ret;
const char *description;
njs_set_undefined(retval);
ret = rmdir(path);
if (ret == 0) {
return NJS_OK;
}
description = strerror(errno);
if (recursive && (errno == ENOTEMPTY || errno == EEXIST)) {
ret = njs_file_tree_walk(path, njs_fs_rmtree_cb, 16,
NJS_FTW_PHYS | NJS_FTW_MOUNT | NJS_FTW_DEPTH);
if (ret == NJS_OK) {
return NJS_OK;
}
description = strerror(errno);
}
return njs_fs_error(vm, "rmdir", description, path, errno, retval);
}
static const char *
njs_fs_path(njs_vm_t *vm, char storage[NJS_MAX_PATH + 1],
const njs_value_t *src, const char *prop_name)
{
u_char *p;
njs_str_t str;
njs_typed_array_t *array;
njs_array_buffer_t *buffer;
switch (src->type) {
case NJS_STRING:
njs_string_get(src, &str);
break;
case NJS_TYPED_ARRAY:
case NJS_DATA_VIEW:
array = njs_typed_array(src);
buffer = array->buffer;
if (njs_slow_path(njs_is_detached_buffer(buffer))) {
njs_type_error(vm, "detached buffer");
return NULL;
}
str.start = &buffer->u.u8[array->offset];
str.length = array->byte_length;
break;
default:
njs_type_error(vm, "\"%s\" must be a string or Buffer", prop_name);
return NULL;
}
if (njs_slow_path(str.length > NJS_MAX_PATH - 1)) {
njs_type_error(vm, "\"%s\" is too long >= %d", prop_name, NJS_MAX_PATH);
return NULL;
}
if (njs_slow_path(memchr(str.start, '\0', str.length) != 0)) {
njs_type_error(vm, "\"%s\" must be a Buffer without null bytes",
prop_name);
return NULL;
}
p = njs_cpymem(storage, str.start, str.length);
*p++ = '\0';
return storage;
}
static int
njs_fs_flags(njs_vm_t *vm, njs_value_t *value, int default_flags)
{
njs_str_t flags;
njs_int_t ret;
njs_fs_entry_t *fl;
if (njs_is_undefined(value)) {
return default_flags;
}
ret = njs_value_to_string(vm, value, value);
if (njs_slow_path(ret != NJS_OK)) {
return -1;
}
njs_string_get(value, &flags);
for (fl = &njs_flags_table[0]; fl->name.length != 0; fl++) {
if (njs_strstr_eq(&flags, &fl->name)) {
return fl->value;
}
}
njs_type_error(vm, "Unknown file open flags: \"%V\"", &flags);
return -1;
}
static mode_t
njs_fs_mode(njs_vm_t *vm, njs_value_t *value, mode_t default_mode)
{
uint32_t u32;
njs_int_t ret;
/* GCC complains about uninitialized u32. */
u32 = 0;
if (njs_is_undefined(value)) {
return default_mode;
}
ret = njs_value_to_uint32(vm, value, &u32);
if (njs_slow_path(ret != NJS_OK)) {
return (mode_t) -1;
}
return (mode_t) u32;
}
static njs_int_t
njs_fs_error(njs_vm_t *vm, const char *syscall, const char *description,
const char *path, int errn, njs_value_t *retval)
{
size_t size;
njs_int_t ret;
njs_value_t value;
const char *code;
njs_object_t *error;
static const njs_value_t string_errno = njs_string("errno");
static const njs_value_t string_code = njs_string("code");
static const njs_value_t string_path = njs_string("path");
static const njs_value_t string_syscall = njs_string("syscall");
size = description != NULL ? njs_strlen(description) : 0;
ret = njs_string_create(vm, &value, description, size);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
error = njs_error_alloc(vm, NJS_OBJ_TYPE_ERROR, NULL, &value, NULL);
if (njs_slow_path(error == NULL)) {
return NJS_ERROR;
}
njs_set_object(retval, error);
if (errn != 0) {
njs_set_number(&value, errn);
ret = njs_value_property_set(vm, retval, njs_value_arg(&string_errno),
&value);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
code = njs_errno_string(errn);
ret = njs_string_create(vm, &value, code, njs_strlen(code));
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
ret = njs_value_property_set(vm, retval, njs_value_arg(&string_code),
&value);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
}
if (path != NULL) {
ret = njs_string_create(vm, &value, path, njs_strlen(path));
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
ret = njs_value_property_set(vm, retval, njs_value_arg(&string_path),
&value);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
}
if (syscall != NULL) {
ret = njs_string_create(vm, &value, syscall, njs_strlen(syscall));
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
ret = njs_value_property_set(vm, retval, njs_value_arg(&string_syscall),
&value);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
}
return NJS_OK;
}
static njs_int_t
ngx_fs_promise_trampoline(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused)
{
njs_value_t value;
return njs_function_call(vm, njs_function(&args[1]), &njs_value_undefined,
&args[2], 1, &value);
}
static const njs_value_t promise_trampoline =
njs_native_function(ngx_fs_promise_trampoline, 2);
static njs_int_t
njs_fs_result(njs_vm_t *vm, njs_value_t *result, njs_index_t calltype,
const njs_value_t *callback, njs_uint_t nargs)
{
njs_int_t ret;
njs_value_t promise, callbacks[2], arguments[2];
switch (calltype) {
case NJS_FS_DIRECT:
vm->retval = *result;
return njs_is_error(result) ? NJS_ERROR : NJS_OK;
case NJS_FS_PROMISE:
ret = njs_vm_promise_create(vm, &promise, &callbacks[0]);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
arguments[0] = njs_is_error(result) ? callbacks[1] : callbacks[0];
arguments[1] = *result;
ret = njs_fs_add_event(vm, njs_value_arg(&promise_trampoline),
njs_value_arg(&arguments), 2);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
vm->retval = promise;
return NJS_OK;
case NJS_FS_CALLBACK:
if (njs_is_error(result)) {
arguments[0] = *result;
njs_set_undefined(&arguments[1]);
} else {
njs_set_undefined(&arguments[0]);
arguments[1] = *result;
}
ret = njs_fs_add_event(vm, callback, njs_value_arg(&arguments),
nargs);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
njs_set_undefined(&vm->retval);
return NJS_OK;
default:
njs_internal_error(vm, "invalid calltype");
return NJS_ERROR;
}
}
static njs_int_t
njs_fs_add_event(njs_vm_t *vm, const njs_value_t *callback,
const njs_value_t *args, njs_uint_t nargs)
{
njs_event_t *event;
njs_vm_ops_t *ops;
ops = vm->options.ops;
if (njs_slow_path(ops == NULL)) {
njs_internal_error(vm, "not supported by host environment");
return NJS_ERROR;
}
event = njs_mp_alloc(vm->mem_pool, sizeof(njs_event_t));
if (njs_slow_path(event == NULL)) {
goto memory_error;
}
event->destructor = ops->clear_timer;
event->function = njs_function(callback);
event->nargs = nargs;
event->once = 1;
event->posted = 0;
event->args = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t) * nargs);
if (njs_slow_path(event->args == NULL)) {
goto memory_error;
}
memcpy(event->args, args, sizeof(njs_value_t) * nargs);
event->host_event = ops->set_timer(vm->external, 0, event);
if (njs_slow_path(event->host_event == NULL)) {
njs_internal_error(vm, "set_timer() failed");
return NJS_ERROR;
}
return njs_add_event(vm, event);
memory_error:
njs_memory_error(vm);
return NJS_ERROR;
}
static njs_int_t
njs_fs_dirent_create(njs_vm_t *vm, njs_value_t *name, njs_value_t *type,
njs_value_t *retval)
{
njs_int_t ret;
static const njs_value_t string_name = njs_string("name");
static const njs_value_t string_type = njs_string("type");
ret = njs_vm_external_create(vm, retval, njs_fs_dirent_proto_id, NULL, 0);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
ret = njs_value_property_set(vm, retval, njs_value_arg(&string_name),
name);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
/* TODO: use a private symbol as a key. */
return njs_value_property_set(vm, retval, njs_value_arg(&string_type),
type);
}
static njs_int_t
njs_fs_dirent_constructor(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused)
{
if (njs_slow_path(!vm->top_frame->ctor)) {
njs_type_error(vm, "the Dirent constructor must be called with new");
return NJS_ERROR;
}
return njs_fs_dirent_create(vm, njs_arg(args, nargs, 1),
njs_arg(args, nargs, 2), &vm->retval);
}
static const njs_object_prop_t njs_dirent_constructor_properties[] =
{
{
.type = NJS_PROPERTY,
.name = njs_string("name"),
.value = njs_string("Dirent"),
.configurable = 1,
},
{
.type = NJS_PROPERTY,
.name = njs_string("length"),
.value = njs_value(NJS_NUMBER, 1, 2.0),
.configurable = 1,
},
{
.type = NJS_PROPERTY_HANDLER,
.name = njs_string("prototype"),
.value = njs_prop_handler(njs_object_prototype_create),
},
};
const njs_object_init_t njs_dirent_constructor_init = {
njs_dirent_constructor_properties,
njs_nitems(njs_dirent_constructor_properties),
};
static njs_int_t
njs_fs_dirent_test(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t testtype)
{
njs_int_t ret;
njs_value_t type, *this;
static const njs_value_t string_type = njs_string("type");
this = njs_argument(args, 0);
ret = njs_value_property(vm, this, njs_value_arg(&string_type), &type);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
if (njs_slow_path(njs_is_number(&type)
&& (njs_number(&type) == NJS_DT_INVALID)))
{
njs_internal_error(vm, "dentry type is not supported on this platform");
return NJS_ERROR;
}
njs_set_boolean(&vm->retval,
njs_is_number(&type) && testtype == njs_number(&type));
return NJS_OK;
}
static void
njs_fs_to_stat(njs_stat_t *dst, struct stat *st)
{
dst->st_dev = st->st_dev;
dst->st_mode = st->st_mode;
dst->st_nlink = st->st_nlink;
dst->st_uid = st->st_uid;
dst->st_gid = st->st_gid;
dst->st_rdev = st->st_rdev;
dst->st_ino = st->st_ino;
dst->st_size = st->st_size;
dst->st_blksize = st->st_blksize;
dst->st_blocks = st->st_blocks;
#if (NJS_HAVE_STAT_ATIMESPEC)
dst->st_atim.tv_sec = st->st_atimespec.tv_sec;
dst->st_atim.tv_nsec = st->st_atimespec.tv_nsec;
dst->st_mtim.tv_sec = st->st_mtimespec.tv_sec;
dst->st_mtim.tv_nsec = st->st_mtimespec.tv_nsec;
dst->st_ctim.tv_sec = st->st_ctimespec.tv_sec;
dst->st_ctim.tv_nsec = st->st_ctimespec.tv_nsec;
dst->st_birthtim.tv_sec = st->st_birthtimespec.tv_sec;
dst->st_birthtim.tv_nsec = st->st_birthtimespec.tv_nsec;
#elif (NJS_HAVE_STAT_ATIM)
dst->st_atim.tv_sec = st->st_atim.tv_sec;
dst->st_atim.tv_nsec = st->st_atim.tv_nsec;
dst->st_mtim.tv_sec = st->st_mtim.tv_sec;
dst->st_mtim.tv_nsec = st->st_mtim.tv_nsec;
dst->st_ctim.tv_sec = st->st_ctim.tv_sec;
dst->st_ctim.tv_nsec = st->st_ctim.tv_nsec;
#if (NJS_HAVE_STAT_BIRTHTIM)
dst->st_birthtim.tv_sec = st->st_birthtim.tv_sec;
dst->st_birthtim.tv_nsec = st->st_birthtim.tv_nsec;
#else
dst->st_birthtim.tv_sec = st->st_ctim.tv_sec;
dst->st_birthtim.tv_nsec = st->st_ctim.tv_nsec;
#endif
#else
dst->st_atim.tv_sec = st->st_atime;
dst->st_atim.tv_nsec = 0;
dst->st_mtim.tv_sec = st->st_mtime;
dst->st_mtim.tv_nsec = 0;
dst->st_ctim.tv_sec = st->st_ctime;
dst->st_ctim.tv_nsec = 0;
dst->st_birthtim.tv_sec = st->st_ctime;
dst->st_birthtim.tv_nsec = 0;
#endif
}
static njs_int_t
njs_fs_stats_create(njs_vm_t *vm, struct stat *st, njs_value_t *retval)
{
njs_stat_t *stat;
stat = njs_mp_alloc(vm->mem_pool, sizeof(njs_stat_t));
if (njs_slow_path(stat == NULL)) {
njs_memory_error(vm);
return NJS_ERROR;
}
njs_fs_to_stat(stat, st);
return njs_vm_external_create(vm, retval, njs_fs_stats_proto_id,
stat, 0);
}
static njs_int_t
njs_fs_stats_test(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t testtype)
{
unsigned mask;
njs_stat_t *st;
st = njs_vm_external(vm, njs_fs_stats_proto_id, njs_argument(args, 0));
if (njs_slow_path(st == NULL)) {
return NJS_DECLINED;
}
switch (testtype) {
case DT_DIR:
mask = S_IFDIR;
break;
case DT_REG:
mask = S_IFREG;
break;
case DT_CHR:
mask = S_IFCHR;
break;
case DT_LNK:
mask = S_IFLNK;
break;
case DT_BLK:
mask = S_IFBLK;
break;
case DT_FIFO:
mask = S_IFIFO;
break;
case DT_SOCK:
default:
mask = S_IFSOCK;
}
njs_set_boolean(&vm->retval, (st->st_mode & S_IFMT) == mask);
return NJS_OK;
}
static njs_int_t
njs_fs_stats_prop(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value,
njs_value_t *setval, njs_value_t *retval)
{
double v;
njs_date_t *date;
njs_stat_t *st;
#define njs_fs_time_ms(ts) ((ts)->tv_sec * 1000.0 + (ts)->tv_nsec / 1000000.0)
st = njs_vm_external(vm, njs_fs_stats_proto_id, value);
if (njs_slow_path(st == NULL)) {
return NJS_DECLINED;
}
switch (prop->value.data.magic32 & 0xf) {
case NJS_FS_STAT_DEV:
v = st->st_dev;
break;
case NJS_FS_STAT_INO:
v = st->st_ino;
break;
case NJS_FS_STAT_MODE:
v = st->st_mode;
break;
case NJS_FS_STAT_NLINK:
v = st->st_nlink;
break;
case NJS_FS_STAT_UID:
v = st->st_uid;
break;
case NJS_FS_STAT_GID:
v = st->st_gid;
break;
case NJS_FS_STAT_RDEV:
v = st->st_rdev;
break;
case NJS_FS_STAT_SIZE:
v = st->st_size;
break;
case NJS_FS_STAT_BLKSIZE:
v = st->st_blksize;
break;
case NJS_FS_STAT_BLOCKS:
v = st->st_blocks;
break;
case NJS_FS_STAT_ATIME:
v = njs_fs_time_ms(&st->st_atim);
break;
case NJS_FS_STAT_BIRTHTIME:
v = njs_fs_time_ms(&st->st_birthtim);
break;
case NJS_FS_STAT_CTIME:
v = njs_fs_time_ms(&st->st_ctim);
break;
case NJS_FS_STAT_MTIME:
default:
v = njs_fs_time_ms(&st->st_mtim);
break;
}
switch (prop->value.data.magic32 >> 4) {
case NJS_NUMBER:
njs_set_number(retval, v);
break;
case NJS_DATE:
default:
date = njs_date_alloc(vm, v);
if (njs_slow_path(date == NULL)) {
return NJS_ERROR;
}
njs_set_date(retval, date);
break;
}
return NJS_OK;
}
static const njs_object_prop_t njs_fs_promises_properties[] =
{
{
.type = NJS_PROPERTY,
.name = njs_string("readFile"),
.value = njs_native_function2(njs_fs_read, 0, NJS_FS_PROMISE),
.writable = 1,
.configurable = 1,
},
{
.type = NJS_PROPERTY,
.name = njs_string("appendFile"),
.value = njs_native_function2(njs_fs_write, 0,
njs_fs_magic(NJS_FS_PROMISE, NJS_FS_APPEND)),
.writable = 1,
.configurable = 1,
},
{
.type = NJS_PROPERTY,
.name = njs_string("writeFile"),
.value = njs_native_function2(njs_fs_write, 0,
njs_fs_magic(NJS_FS_PROMISE, NJS_FS_TRUNC)),
.writable = 1,
.configurable = 1,
},
{
.type = NJS_PROPERTY,
.name = njs_string("access"),
.value = njs_native_function2(njs_fs_access, 0, NJS_FS_PROMISE),
.writable = 1,
.configurable = 1,
},
{
.type = NJS_PROPERTY,
.name = njs_string("mkdir"),
.value = njs_native_function2(njs_fs_mkdir, 0, NJS_FS_PROMISE),
.writable = 1,
.configurable = 1,
},
{
.type = NJS_PROPERTY,
.name = njs_string("rename"),
.value = njs_native_function2(njs_fs_rename, 0, NJS_FS_PROMISE),
.writable = 1,
.configurable = 1,
},
{
.type = NJS_PROPERTY,
.name = njs_string("rmdir"),
.value = njs_native_function2(njs_fs_rmdir, 0, NJS_FS_PROMISE),
.writable = 1,
.configurable = 1,
},
{
.type = NJS_PROPERTY,
.name = njs_string("readdir"),
.value = njs_native_function2(njs_fs_readdir, 0, NJS_FS_PROMISE),
.writable = 1,
.configurable = 1,
},
{
.type = NJS_PROPERTY,
.name = njs_string("lstat"),
.value = njs_native_function2(njs_fs_stat, 0,
njs_fs_magic(NJS_FS_PROMISE, NJS_FS_LSTAT)),
.writable = 1,
.configurable = 1,
},
{
.type = NJS_PROPERTY,
.name = njs_string("stat"),
.value = njs_native_function2(njs_fs_stat, 0,
njs_fs_magic(NJS_FS_PROMISE, NJS_FS_STAT)),
.writable = 1,
.configurable = 1,
},
{
.type = NJS_PROPERTY,
.name = njs_string("symlink"),
.value = njs_native_function2(njs_fs_symlink, 0, NJS_FS_PROMISE),
.writable = 1,
.configurable = 1,
},
{
.type = NJS_PROPERTY,
.name = njs_string("unlink"),
.value = njs_native_function2(njs_fs_unlink, 0, NJS_FS_PROMISE),
.writable = 1,
.configurable = 1,
},
{
.type = NJS_PROPERTY,
.name = njs_string("realpath"),
.value = njs_native_function2(njs_fs_realp