From e2b381154c0f89a4ee23190e2e7d7cae105587e6 Mon Sep 17 00:00:00 2001 From: Sylvain Henry <sylvain@haskus.fr> Date: Wed, 19 Jul 2023 14:33:47 +0200 Subject: [PATCH] JS: implement openat(AT_FDCWD...) (#23697) Use `openSync` to implement `openat(AT_FDCWD...)`. --- libraries/base/jsbits/base.js | 162 +++++++++++++++++++++-------- libraries/base/jsbits/errno.js | 1 + libraries/base/tests/Makefile | 6 ++ libraries/base/tests/T23697.hsc | 37 +++++++ libraries/base/tests/T23697.stdout | 1 + libraries/base/tests/all.T | 4 + 6 files changed, 168 insertions(+), 43 deletions(-) create mode 100644 libraries/base/tests/T23697.hsc create mode 100644 libraries/base/tests/T23697.stdout diff --git a/libraries/base/jsbits/base.js b/libraries/base/jsbits/base.js index 587fda0e9f74..3cd79fde2851 100644 --- a/libraries/base/jsbits/base.js +++ b/libraries/base/jsbits/base.js @@ -39,7 +39,13 @@ function h$base_chmod(file, file_off, mode, c) { } function h$base_close(fd, c) { - TRACE_IO("base_close fd: " + fd) + TRACE_IO("base_close fd: " + fd) + return h$close(fd,c); +} + +function h$close(fd,c) { + if (c) { + //asynchronous var fdo = h$base_fds[fd]; if(fdo) { delete h$base_fds[fd]; @@ -59,6 +65,16 @@ function h$base_close(fd, c) { h$errno = CONST_EINVAL; c(-1); } + } else { + //synchronous + try { + h$fs.closeSync(fd); + return 0; + } catch(err) { + h$setErrno(err); + return (-1); + } + } } function h$base_dup(fd, c) { @@ -265,11 +281,25 @@ function h$rmdir(file, file_off) { } function h$base_open(file, file_off, how, mode, c) { + return h$open(file,file_off,how,mode,c); +} + +function h$openat(dirfd, file, file_off, how, mode) { + if (dirfd != h$base_at_fdcwd) { + // we only support AT_FDWCD (open) until NodeJS provides "openat" + return h$unsupported(-1); + } + else { + return h$open(file,file_off,how,mode,undefined); + } +} + +function h$open(file, file_off, how, mode,c) { #ifndef GHCJS_BROWSER if(h$isNode()) { var flags, off; var fp = h$decodeUtf8z(file, file_off); - TRACE_IO("base_open: " + fp) + TRACE_IO("open: " + fp) var acc = how & h$base_o_accmode; // passing a number lets node.js use it directly as the flags (undocumented) if(acc === h$base_o_rdonly) { @@ -284,34 +314,64 @@ function h$base_open(file, file_off, how, mode, c) { | ((how & h$base_o_creat) ? h$processConstants['fs']['O_CREAT'] : 0) | ((how & h$base_o_excl) ? h$processConstants['fs']['O_EXCL'] : 0) | ((how & h$base_o_append) ? h$processConstants['fs']['O_APPEND'] : 0); - h$fs.open(fp, flags, mode, function(err, fd) { - if(err) { - h$handleErrnoC(err, -1, 0, c); + if (c) { + // asynchronous + h$fs.open(fp, flags, mode, function(err, fd) { + if(err) { + h$handleErrnoC(err, -1, 0, c); + } else { + var f = function(p) { + h$base_fds[fd] = { read: h$base_readFile + , write: h$base_writeFile + , close: h$base_closeFile + , fd: fd + , pos: p + , refs: 1 + }; + TRACE_IO("base_open: " + fp + " -> " + fd) + c(fd); + } + if(off === -1) { + h$fs.stat(fp, function(err, fs) { + if(err) h$handleErrnoC(err, -1, 0, c); else f(fs.size); + }); + } else { + f(0); + } + } + }); + } + else { + // synchronous + try { + var fd = h$fs.openSync(fp, flags, mode); + var f = function(p) { + h$base_fds[fd] = { read: h$base_readFile + , write: h$base_writeFile + , close: h$base_closeFile + , fd: fd + , pos: p + , refs: 1 + }; + TRACE_IO("open: " + fp + " -> " + fd) + } + if(off === -1) { + var fs = h$fs.statSync(fp); + f(fs.size); } else { - var f = function(p) { - h$base_fds[fd] = { read: h$base_readFile - , write: h$base_writeFile - , close: h$base_closeFile - , fd: fd - , pos: p - , refs: 1 - }; - TRACE_IO("base_open: " + fp + " -> " + fd) - c(fd); - } - if(off === -1) { - h$fs.stat(fp, function(err, fs) { - if(err) h$handleErrnoC(err, -1, 0, c); else f(fs.size); - }); - } else { - f(0); - } + f(0); } - }); + return fd; + } catch(err) { + h$setErrno(err); + return -1; + } + } } else #endif - h$unsupported(-1, c); + return h$unsupported(-1,c); } + function h$base_read(fd, buf, buf_off, n, c) { TRACE_IO("base_read: " + fd) var fdo = h$base_fds[fd]; @@ -353,16 +413,30 @@ function h$base_write(fd, buf, buf_off, n, c) { // buf_off: offset in the buffer // n: number of bytes to write // c: continuation - TRACE_IO("base_write: " + fd) + TRACE_IO("base_write: " + fd) + return h$write(fd,buf,buf_off,n,c); +} - var fdo = h$base_fds[fd]; +function h$write(fd, buf, buf_off, n, c) { - if(fdo && fdo.write) { - fdo.write(fd, fdo, buf, buf_off, n, c); + if (c) { + var fdo = h$base_fds[fd]; + // asynchronous + if(fdo && fdo.write) { + fdo.write(fd, fdo, buf, buf_off, n, c); + } else { + h$fs.write(fd, buf.u8, buf_off, n, function(err, bytesWritten, buf0) { + h$handleErrnoC(err, -1, bytesWritten, c); + }); + } } else { - h$fs.write(fd, buf.u8, buf_off, n, function(err, bytesWritten, buf0) { - h$handleErrnoC(err, -1, bytesWritten, c); - }); + //synchronous + try { + return h$fs.writeSync(fd, buf.u8, buf_off, n); + } catch(err) { + h$setErrno(err); + return (-1); + } } } @@ -447,17 +521,19 @@ function h$base_utime(file, file_off, timbuf, timbuf_off, c) { function h$base_waitpid(pid, stat, stat_off, options, c) { throw "h$base_waitpid"; } -/** @const */ var h$base_o_rdonly = 0x00000; -/** @const */ var h$base_o_wronly = 0x00001; -/** @const */ var h$base_o_rdwr = 0x00002; -/** @const */ var h$base_o_accmode = 0x00003; -/** @const */ var h$base_o_append = 0x00008; -/** @const */ var h$base_o_creat = 0x00200; -/** @const */ var h$base_o_trunc = 0x00400; -/** @const */ var h$base_o_excl = 0x00800; -/** @const */ var h$base_o_noctty = 0x20000; -/** @const */ var h$base_o_nonblock = 0x00004; -/** @const */ var h$base_o_binary = 0x00000; +const h$base_o_rdonly = 0x00000; +const h$base_o_wronly = 0x00001; +const h$base_o_rdwr = 0x00002; +const h$base_o_accmode = 0x00003; +const h$base_o_append = 0x00008; +const h$base_o_creat = 0x00200; +const h$base_o_trunc = 0x00400; +const h$base_o_excl = 0x00800; +const h$base_o_noctty = 0x20000; +const h$base_o_nonblock = 0x00004; +const h$base_o_binary = 0x00000; +const h$base_at_fdcwd = -100; + function h$base_stat_check_mode(mode,p) { // inspired by Node's checkModeProperty diff --git a/libraries/base/jsbits/errno.js b/libraries/base/jsbits/errno.js index 30401d915628..b7ea1fb9ae01 100644 --- a/libraries/base/jsbits/errno.js +++ b/libraries/base/jsbits/errno.js @@ -51,6 +51,7 @@ function h$setErrno(e) { if(es.indexOf('ESPIPE') !== -1) return CONST_ESPIPE; if(es.indexOf('EBADF') !== -1) return CONST_EBADF; if(es.indexOf('ENOSPC') !== -1) return CONST_ENOSPC; + if(es.indexOf('EACCES') !== -1) return CONST_EACCES; if(es.indexOf('Bad argument') !== -1) return CONST_ENOENT; // fixme? throw ("setErrno not yet implemented for: " + e); diff --git a/libraries/base/tests/Makefile b/libraries/base/tests/Makefile index 6a0abcf1cf7f..443ce1363a3c 100644 --- a/libraries/base/tests/Makefile +++ b/libraries/base/tests/Makefile @@ -5,3 +5,9 @@ TOP=../../../testsuite include $(TOP)/mk/boilerplate.mk include $(TOP)/mk/test.mk + +.PHONY: T23697 +T23697: + '$(HSC2HS)' T23697.hsc + '$(TEST_HC)' $(TEST_HC_OPTS) $(WAY_FLAGS) -v0 T23697.hs + ./T23697 diff --git a/libraries/base/tests/T23697.hsc b/libraries/base/tests/T23697.hsc new file mode 100644 index 000000000000..b9becb638d39 --- /dev/null +++ b/libraries/base/tests/T23697.hsc @@ -0,0 +1,37 @@ +{-# LANGUAGE MagicHash #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE NegativeLiterals #-} + +module Main (main) where + +import Foreign.C.Types +import Foreign.C.String +import Foreign.Ptr +import GHC.Ptr +import System.Posix.Internals +import Data.Bits + +#include<fcntl.h> + +main :: IO () +main = do + + let + checkErr :: (Num a, Eq a) => a -> a + checkErr = \case + (-1) -> error "Returned (-1)" + x -> x + + fname = "foo" + + -- test: openat(AT_FDCWD...), write, close + withCString fname $ \cstr -> do + fd <- checkErr <$> openat (#const AT_FDCWD) cstr (o_WRONLY .|. o_CREAT) 0o666 + checkErr <$> write fd (Ptr "123456"##) 6 + checkErr <$> close fd + + putStrLn =<< readFile fname + +foreign import ccall openat :: CInt -> CString -> CInt -> CUInt -> IO CInt +foreign import ccall write :: CInt -> Ptr () -> CSize -> IO CInt +foreign import ccall close :: CInt -> IO CInt diff --git a/libraries/base/tests/T23697.stdout b/libraries/base/tests/T23697.stdout new file mode 100644 index 000000000000..9f358a4addef --- /dev/null +++ b/libraries/base/tests/T23697.stdout @@ -0,0 +1 @@ +123456 diff --git a/libraries/base/tests/all.T b/libraries/base/tests/all.T index dfeb95a8f0ed..538018ae32d6 100644 --- a/libraries/base/tests/all.T +++ b/libraries/base/tests/all.T @@ -313,3 +313,7 @@ test('AtomicModifyIORef', normal, compile_and_run, ['']) test('AtomicSwapIORef', normal, compile_and_run, ['']) test('T23454', normal, compile_fail, ['']) test('T23687', normal, compile_and_run, ['']) +test('T23697', + [ when(opsys('mingw32'), skip) # header not found + , when(opsys('darwin'), skip) # permission denied + ], makefile_test, ['T23697']) -- GitLab