Skip to content

Commit 006aa37

Browse files
committed
Merge pull request #2208 from bettio/more-posix-file-funcs
Add POSIX file operation NIFs Extend the atomvm module with additional POSIX file operation NIFs: seek, pread, pwrite, fsync, ftruncate, rename, stat, fstat, mkdir and rmdir. Export the existing posix_mkfifo NIF from atomvm.erl. Highlights: - Fix unchecked globalcontext_make_atom calls in posix_nifs - Fix read count validation to reject negative values and signed/unsigned comparison - Add ENOTEMPTY errno mapping - Tighten mkfifo and mkdir mode validation These changes are made under both the "Apache 2.0" and the "GNU Lesser General Public License 2.1 or later" license terms (dual license). SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
2 parents ac42384 + 698eaf2 commit 006aa37

10 files changed

Lines changed: 1006 additions & 41 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ encoding/decoding options, also Elixir `(url_)encode64`/`(url_)decode64` have be
102102
(`AVM_USE_LIBSODIUM=ON`)
103103
- Added support for `bag` and `duplicate_bag` table types in `ets`
104104
- Added `ets:insert_new/2`, `ets:member/2`, `ets:lookup_element/4`, `ets:delete_object/2`, `ets:take/2`, `ets:update_element/3` and `ets:update_element/4`
105+
- Added POSIX file functions: `atomvm:posix_seek/3`, `atomvm:posix_pread/3`,
106+
`atomvm:posix_pwrite/3`, `atomvm:posix_fsync/1`, `atomvm:posix_ftruncate/2`,
107+
`atomvm:posix_rename/2`, `atomvm:posix_stat/1`, `atomvm:posix_fstat/1`
108+
- Added POSIX directory functions: `atomvm:posix_mkdir/1`, `atomvm:posix_rmdir/1`,
105109

106110
### Changed
107111

libs/eavmlib/src/atomvm.erl

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,17 @@
4040
posix_close/1,
4141
posix_read/2,
4242
posix_write/2,
43+
posix_seek/3,
44+
posix_pread/3,
45+
posix_pwrite/3,
46+
posix_fsync/1,
47+
posix_ftruncate/2,
48+
posix_mkfifo/2,
49+
posix_mkdir/2,
50+
posix_rmdir/1,
51+
posix_rename/2,
52+
posix_stat/1,
53+
posix_fstat/1,
4354
posix_clock_settime/2,
4455
posix_opendir/1,
4556
posix_closedir/1,
@@ -51,6 +62,8 @@
5162
-export_type([
5263
posix_fd/0,
5364
posix_open_flag/0,
65+
posix_whence/0,
66+
posix_stat_info/0,
5467
posix_dir/0
5568
]).
5669

@@ -89,6 +102,22 @@
89102
-type posix_error() ::
90103
atom()
91104
| integer().
105+
-type posix_whence() ::
106+
seek_set
107+
| seek_cur
108+
| seek_end.
109+
-type posix_stat_info() :: #{
110+
st_dev := integer(),
111+
st_ino := integer(),
112+
st_mode := integer(),
113+
st_nlink := integer(),
114+
st_uid := integer(),
115+
st_gid := integer(),
116+
st_size := integer(),
117+
st_atime_s := integer(),
118+
st_mtime_s := integer(),
119+
st_ctime_s := integer()
120+
}.
92121

93122
-opaque posix_dir() :: binary().
94123

@@ -282,6 +311,134 @@ posix_read(_File, _Count) ->
282311
posix_write(_File, _Data) ->
283312
erlang:nif_error(undefined).
284313

314+
%%-----------------------------------------------------------------------------
315+
%% @param File Descriptor to an open file
316+
%% @param Offset Offset in bytes
317+
%% @param Whence Reference point for the offset
318+
%% @returns a tuple with the resulting absolute offset or an error tuple
319+
%% @doc Reposition the file cursor using `lseek(2)'.
320+
%% @end
321+
%%-----------------------------------------------------------------------------
322+
-spec posix_seek(File :: posix_fd(), Offset :: integer(), Whence :: posix_whence()) ->
323+
{ok, integer()} | {error, posix_error()}.
324+
posix_seek(_File, _Offset, _Whence) ->
325+
erlang:nif_error(undefined).
326+
327+
%%-----------------------------------------------------------------------------
328+
%% @param File Descriptor to an open file
329+
%% @param Count Maximum number of bytes to read
330+
%% @param Offset Offset in bytes from the start of the file
331+
%% @returns a tuple with read bytes, `eof' or an error tuple
332+
%% @doc Read at most `Count' bytes from a file at a given offset using
333+
%% `pread(2)'. The file cursor is not modified.
334+
%% @end
335+
%%-----------------------------------------------------------------------------
336+
-spec posix_pread(File :: posix_fd(), Count :: non_neg_integer(), Offset :: non_neg_integer()) ->
337+
{ok, binary()} | eof | {error, posix_error()}.
338+
posix_pread(_File, _Count, _Offset) ->
339+
erlang:nif_error(undefined).
340+
341+
%%-----------------------------------------------------------------------------
342+
%% @param File Descriptor to an open file
343+
%% @param Data Data to write
344+
%% @param Offset Offset in bytes from the start of the file
345+
%% @returns a tuple with the number of written bytes or an error tuple
346+
%% @doc Write data to a file at a given offset using `pwrite(2)'.
347+
%% The file cursor is not modified.
348+
%% @end
349+
%%-----------------------------------------------------------------------------
350+
-spec posix_pwrite(File :: posix_fd(), Data :: binary(), Offset :: non_neg_integer()) ->
351+
{ok, non_neg_integer()} | {error, posix_error()}.
352+
posix_pwrite(_File, _Data, _Offset) ->
353+
erlang:nif_error(undefined).
354+
355+
%%-----------------------------------------------------------------------------
356+
%% @param File Descriptor to an open file
357+
%% @returns `ok' or an error tuple
358+
%% @doc Flush file data to storage using `fsync(2)'.
359+
%% @end
360+
%%-----------------------------------------------------------------------------
361+
-spec posix_fsync(File :: posix_fd()) -> ok | {error, posix_error()}.
362+
posix_fsync(_File) ->
363+
erlang:nif_error(undefined).
364+
365+
%%-----------------------------------------------------------------------------
366+
%% @param File Descriptor to an open file
367+
%% @param Length Desired file length in bytes
368+
%% @returns `ok' or an error tuple
369+
%% @doc Truncate a file to a specified length using `ftruncate(2)'.
370+
%% If the file is larger than `Length', the extra data is lost. If the file
371+
%% is shorter, it is extended with null bytes.
372+
%% @end
373+
%%-----------------------------------------------------------------------------
374+
-spec posix_ftruncate(File :: posix_fd(), Length :: non_neg_integer()) ->
375+
ok | {error, posix_error()}.
376+
posix_ftruncate(_File, _Length) ->
377+
erlang:nif_error(undefined).
378+
379+
%%-----------------------------------------------------------------------------
380+
%% @param Path Path to the new FIFO special file
381+
%% @param Mode Permission bits for the new FIFO
382+
%% @returns `ok' or an error tuple
383+
%% @doc Create a FIFO special file (named pipe) using `mkfifo(2)'.
384+
%% @end
385+
%%-----------------------------------------------------------------------------
386+
-spec posix_mkfifo(Path :: iodata(), Mode :: non_neg_integer()) ->
387+
ok | {error, posix_error()}.
388+
posix_mkfifo(_Path, _Mode) ->
389+
erlang:nif_error(undefined).
390+
391+
%%-----------------------------------------------------------------------------
392+
%% @param Path Path to the directory to create
393+
%% @param Mode Permission bits for the new directory
394+
%% @returns `ok' or an error tuple
395+
%% @doc Create a directory using `mkdir(2)'.
396+
%% @end
397+
%%-----------------------------------------------------------------------------
398+
-spec posix_mkdir(Path :: iodata(), Mode :: non_neg_integer()) ->
399+
ok | {error, posix_error()}.
400+
posix_mkdir(_Path, _Mode) ->
401+
erlang:nif_error(undefined).
402+
403+
%%-----------------------------------------------------------------------------
404+
%% @param Path Path to the directory to remove
405+
%% @returns ok or an error tuple
406+
%% @doc Remove an empty directory using `rmdir(2)'.
407+
%% @end
408+
%%-----------------------------------------------------------------------------
409+
-spec posix_rmdir(Path :: iodata()) ->
410+
ok | {error, posix_error()}.
411+
posix_rmdir(_Path) ->
412+
erlang:nif_error(undefined).
413+
414+
%%-----------------------------------------------------------------------------
415+
-spec posix_rename(OldPath :: iodata(), NewPath :: iodata()) ->
416+
ok | {error, posix_error()}.
417+
posix_rename(_OldPath, _NewPath) ->
418+
erlang:nif_error(undefined).
419+
420+
%%-----------------------------------------------------------------------------
421+
%% @param Path Path to the file
422+
%% @returns a tuple with a map of file information or an error tuple
423+
%% @doc Get file status using `stat(2)'.
424+
%% @end
425+
%%-----------------------------------------------------------------------------
426+
-spec posix_stat(Path :: iodata()) ->
427+
{ok, posix_stat_info()} | {error, posix_error()}.
428+
posix_stat(_Path) ->
429+
erlang:nif_error(undefined).
430+
431+
%%-----------------------------------------------------------------------------
432+
%% @param File Descriptor to an open file
433+
%% @returns a tuple with a map of file information or an error tuple
434+
%% @doc Get file status from a file descriptor using `fstat(2)'.
435+
%% @end
436+
%%-----------------------------------------------------------------------------
437+
-spec posix_fstat(File :: posix_fd()) ->
438+
{ok, posix_stat_info()} | {error, posix_error()}.
439+
posix_fstat(_File) ->
440+
erlang:nif_error(undefined).
441+
285442
%%
286443
%% @param ClockId The clock id
287444
%% @param ValueSinceUnixEpoch The value, in specified seconds and nanoseconds,

src/libAtomVM/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,8 +232,18 @@ define_if_function_exists(libAtomVM opendir "dirent.h" PUBLIC HAVE_OPENDIR)
232232
define_if_function_exists(libAtomVM close "unistd.h" PUBLIC HAVE_CLOSE)
233233
define_if_function_exists(libAtomVM closedir "dirent.h" PUBLIC HAVE_CLOSEDIR)
234234
define_if_function_exists(libAtomVM mkfifo "sys/stat.h" PRIVATE HAVE_MKFIFO)
235+
define_if_function_exists(libAtomVM mkdir "sys/stat.h" PRIVATE HAVE_MKDIR)
235236
define_if_function_exists(libAtomVM readdir "dirent.h" PUBLIC HAVE_READDIR)
237+
define_if_function_exists(libAtomVM lseek "unistd.h" PRIVATE HAVE_LSEEK)
238+
define_if_function_exists(libAtomVM pread "unistd.h" PRIVATE HAVE_PREAD)
239+
define_if_function_exists(libAtomVM pwrite "unistd.h" PRIVATE HAVE_PWRITE)
240+
define_if_function_exists(libAtomVM fsync "unistd.h" PRIVATE HAVE_FSYNC)
241+
define_if_function_exists(libAtomVM ftruncate "unistd.h" PRIVATE HAVE_FTRUNCATE)
242+
define_if_function_exists(libAtomVM rename "stdio.h" PRIVATE HAVE_RENAME)
243+
define_if_function_exists(libAtomVM stat "sys/stat.h" PRIVATE HAVE_STAT)
244+
define_if_function_exists(libAtomVM fstat "sys/stat.h" PRIVATE HAVE_FSTAT)
236245
define_if_function_exists(libAtomVM unlink "unistd.h" PRIVATE HAVE_UNLINK)
246+
define_if_function_exists(libAtomVM rmdir "unistd.h" PRIVATE HAVE_RMDIR)
237247
define_if_function_exists(libAtomVM execve "unistd.h" PRIVATE HAVE_EXECVE)
238248
define_if_function_exists(libAtomVM closefrom "unistd.h" PRIVATE HAVE_CLOSEFROM)
239249
define_if_function_exists(libAtomVM getcwd "unistd.h" PUBLIC HAVE_GETCWD)

src/libAtomVM/nifs.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,16 +1012,66 @@ DEFINE_MATH_NIF(tanh)
10121012
#define IF_HAVE_OPEN_CLOSE(expr) NULL
10131013
#define IF_HAVE_EXECVE(expr) NULL
10141014
#endif
1015+
#if HAVE_OPEN && HAVE_CLOSE && HAVE_LSEEK
1016+
#define IF_HAVE_LSEEK(expr) (expr)
1017+
#else
1018+
#define IF_HAVE_LSEEK(expr) NULL
1019+
#endif
1020+
#if HAVE_OPEN && HAVE_CLOSE && HAVE_PREAD
1021+
#define IF_HAVE_PREAD(expr) (expr)
1022+
#else
1023+
#define IF_HAVE_PREAD(expr) NULL
1024+
#endif
1025+
#if HAVE_OPEN && HAVE_CLOSE && HAVE_PWRITE
1026+
#define IF_HAVE_PWRITE(expr) (expr)
1027+
#else
1028+
#define IF_HAVE_PWRITE(expr) NULL
1029+
#endif
1030+
#if HAVE_OPEN && HAVE_CLOSE && HAVE_FSYNC
1031+
#define IF_HAVE_FSYNC(expr) (expr)
1032+
#else
1033+
#define IF_HAVE_FSYNC(expr) NULL
1034+
#endif
1035+
#if HAVE_OPEN && HAVE_CLOSE && HAVE_FTRUNCATE
1036+
#define IF_HAVE_FTRUNCATE(expr) (expr)
1037+
#else
1038+
#define IF_HAVE_FTRUNCATE(expr) NULL
1039+
#endif
10151040
#if HAVE_MKFIFO
10161041
#define IF_HAVE_MKFIFO(expr) (expr)
10171042
#else
10181043
#define IF_HAVE_MKFIFO(expr) NULL
10191044
#endif
1045+
#if HAVE_MKDIR
1046+
#define IF_HAVE_MKDIR(expr) (expr)
1047+
#else
1048+
#define IF_HAVE_MKDIR(expr) NULL
1049+
#endif
10201050
#if HAVE_UNLINK
10211051
#define IF_HAVE_UNLINK(expr) (expr)
10221052
#else
10231053
#define IF_HAVE_UNLINK(expr) NULL
10241054
#endif
1055+
#if HAVE_RMDIR
1056+
#define IF_HAVE_RMDIR(expr) (expr)
1057+
#else
1058+
#define IF_HAVE_RMDIR(expr) NULL
1059+
#endif
1060+
#if HAVE_RENAME
1061+
#define IF_HAVE_RENAME(expr) (expr)
1062+
#else
1063+
#define IF_HAVE_RENAME(expr) NULL
1064+
#endif
1065+
#if HAVE_STAT
1066+
#define IF_HAVE_STAT(expr) (expr)
1067+
#else
1068+
#define IF_HAVE_STAT(expr) NULL
1069+
#endif
1070+
#if HAVE_OPEN && HAVE_CLOSE && HAVE_FSTAT
1071+
#define IF_HAVE_FSTAT(expr) (expr)
1072+
#else
1073+
#define IF_HAVE_FSTAT(expr) NULL
1074+
#endif
10251075
#if HAVE_CLOCK_SETTIME
10261076
#define IF_HAVE_CLOCK_SETTIME_OR_SETTIMEOFDAY(expr) (expr)
10271077
#elif HAVE_SETTIMEOFDAY

src/libAtomVM/nifs.gperf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,18 @@ atomvm:posix_select_read/3, IF_HAVE_OPEN_CLOSE(&atomvm_posix_select_read_nif)
187187
atomvm:posix_select_write/3, IF_HAVE_OPEN_CLOSE(&atomvm_posix_select_write_nif)
188188
atomvm:posix_select_stop/1, IF_HAVE_OPEN_CLOSE(&atomvm_posix_select_stop_nif)
189189
atomvm:subprocess/4, IF_HAVE_EXECVE(&atomvm_subprocess_nif)
190+
atomvm:posix_seek/3, IF_HAVE_LSEEK(&atomvm_posix_seek_nif)
191+
atomvm:posix_pread/3, IF_HAVE_PREAD(&atomvm_posix_pread_nif)
192+
atomvm:posix_pwrite/3, IF_HAVE_PWRITE(&atomvm_posix_pwrite_nif)
193+
atomvm:posix_fsync/1, IF_HAVE_FSYNC(&atomvm_posix_fsync_nif)
194+
atomvm:posix_ftruncate/2, IF_HAVE_FTRUNCATE(&atomvm_posix_ftruncate_nif)
190195
atomvm:posix_mkfifo/2, IF_HAVE_MKFIFO(&atomvm_posix_mkfifo_nif)
196+
atomvm:posix_mkdir/2, IF_HAVE_MKDIR(&atomvm_posix_mkdir_nif)
191197
atomvm:posix_unlink/1, IF_HAVE_UNLINK(&atomvm_posix_unlink_nif)
198+
atomvm:posix_rmdir/1, IF_HAVE_RMDIR(&atomvm_posix_rmdir_nif)
199+
atomvm:posix_rename/2, IF_HAVE_RENAME(&atomvm_posix_rename_nif)
200+
atomvm:posix_stat/1, IF_HAVE_STAT(&atomvm_posix_stat_nif)
201+
atomvm:posix_fstat/1, IF_HAVE_FSTAT(&atomvm_posix_fstat_nif)
192202
atomvm:posix_clock_settime/2, IF_HAVE_CLOCK_SETTIME_OR_SETTIMEOFDAY(&atomvm_posix_clock_settime_nif)
193203
atomvm:posix_opendir/1, IF_HAVE_OPENDIR_READDIR_CLOSEDIR(&atomvm_posix_opendir_nif)
194204
atomvm:posix_closedir/1, IF_HAVE_OPENDIR_READDIR_CLOSEDIR(&atomvm_posix_closedir_nif)

0 commit comments

Comments
 (0)