From 0e11ca30362643565d36030c6ff74af6b9691a8c Mon Sep 17 00:00:00 2001 From: Daniel Widdis Date: Mon, 25 May 2020 10:55:07 -0700 Subject: [PATCH 1/9] Add size_t, ssize_t, and off_t to LibCAPI --- CHANGES.md | 1 + .../com/sun/jna/platform/unix/LibCAPI.java | 104 ++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 7c94a078c1..26017705d9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,6 +15,7 @@ Features * [#1191](https://github.com/java-native-access/jna/pull/1191): Add `c.s.j.p.win32.Advapi32Util#getTokenPrimaryGroup` - [@dbwiddis](https://github.com/dbwiddis). * [#1182](https://github.com/java-native-access/jna/pull/1182): Add `toString` to classes extending `c.s.j.ptr.ByReference` - [@dbwiddis](https://github.com/dbwiddis). * [#1194](https://github.com/java-native-access/jna/pull/1194): Add `GetConsoleScreenBufferInfo`, `ReadConsoleInput` and `WriteConsole` with associated structures to `c.s.j.p.win32.Wincon` - [@rednoah](https://github.com/rednoah). +* [#1202](https://github.com/java-native-access/jna/pull/1202): Add `size_t`, `ssize_t`, and `off_t` to `c.s.j.p.unix.LibCAPI` - [@dbwiddis](https://github.com/dbwiddis). Bug Fixes --------- diff --git a/contrib/platform/src/com/sun/jna/platform/unix/LibCAPI.java b/contrib/platform/src/com/sun/jna/platform/unix/LibCAPI.java index 27da5b7e6b..5808a05287 100644 --- a/contrib/platform/src/com/sun/jna/platform/unix/LibCAPI.java +++ b/contrib/platform/src/com/sun/jna/platform/unix/LibCAPI.java @@ -23,6 +23,9 @@ */ package com.sun.jna.platform.unix; +import com.sun.jna.IntegerType; +import com.sun.jna.Native; + /** * Note: we are using this "intermediate" API in order to allow * Linux-like O/S-es to implement the same API, but maybe using a different @@ -30,6 +33,107 @@ * @author Lyor Goldstein */ public interface LibCAPI extends Reboot, Resource { + + /** + * This is an unsigned integer type used to represent the sizes of objects. + */ + class size_t extends IntegerType { + public static final size_t ZERO = new size_t(); + + private static final long serialVersionUID = 1L; + + public size_t() { + this(0); + } + + public size_t(long value) { + super(Native.SIZE_T_SIZE, value, true); + } + } + + /** + * This is a signed integer type used for a count of bytes or an error + * indication. + */ + class ssize_t extends IntegerType { + public static final ssize_t ZERO = new ssize_t(); + + private static final long serialVersionUID = 1L; + + public ssize_t() { + this(0); + } + + public ssize_t(long value) { + super(Native.SIZE_T_SIZE, value, false); + } + } + + /** + * This is a signed integer type used to represent file sizes. + *

+ * Authors of portable applications should be aware that on 32-bit operating + * systems, the bit width of this type is dependent on compile-time options in + * the end-user's C library. If the library is compiled with + * {@code _FILE_OFFSET_BITS == 64} this type is 64-bit. + *

+ * The parameter {@code ilp32OffBig} permits this type to be defined as 64-bit + * on a 32-bit operating system. + * + * @see IEEE + * Std 1003.1, 2004 (POSIXv6) + * @see IEEE + * Std 1003.1-2017 (POSIX v7) + */ + class off_t extends IntegerType { + public static final off_t ZERO = new off_t(); + + private static final long serialVersionUID = 1L; + + /** + * Create a new {@code off_t} using the default bit width. + */ + public off_t() { + this(0, false); + } + + /** + * Create a new {@code off_t} using the default bit width or optionally 64-bit + * width. + * + * @param ilp32OffBig + * If {@code true}, use 64-bit width. + */ + public off_t(boolean ilp32OffBig) { + this(0, ilp32OffBig); + } + + /** + * Create a new {@code off_t} using the default bit width. + * + * @param value + * The value to set. + */ + public off_t(long value) { + this(value, false); + } + + /** + * Create a new {@code off_t} using the default bit width or optionally 64-bit + * width. + * + * @param value + * The value to set. + * @param ilp32OffBig + * If {@code true}, use 64-bit width. + */ + public off_t(long value, boolean ilp32OffBig) { + super(ilp32OffBig ? 8 : Native.LONG_SIZE, value); + } + } + // see man(2) get/set uid/gid int getuid(); int geteuid(); From 5b445e86d6c78fc923f41ff8abc2b43884a51dc8 Mon Sep 17 00:00:00 2001 From: Daniel Widdis Date: Mon, 25 May 2020 11:33:49 -0700 Subject: [PATCH 2/9] Add tests --- .../com/sun/jna/platform/linux/LibCTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/contrib/platform/test/com/sun/jna/platform/linux/LibCTest.java b/contrib/platform/test/com/sun/jna/platform/linux/LibCTest.java index 1210bb30c7..7c1b800f08 100644 --- a/contrib/platform/test/com/sun/jna/platform/linux/LibCTest.java +++ b/contrib/platform/test/com/sun/jna/platform/linux/LibCTest.java @@ -34,6 +34,10 @@ import com.sun.jna.platform.linux.LibC.Statvfs; import com.sun.jna.platform.linux.LibC.Sysinfo; +import com.sun.jna.platform.unix.LibCAPI.off_t; +import com.sun.jna.platform.unix.LibCAPI.size_t; +import com.sun.jna.platform.unix.LibCAPI.ssize_t; + import java.nio.file.FileStore; import java.nio.file.Files; import java.nio.file.Paths; @@ -45,6 +49,19 @@ */ public class LibCTest extends TestCase { + @Test + public void testSizeTypes() { + long VALUE = 20; + size_t st = new size_t(VALUE); + assertEquals("Wrong size_t value", VALUE, st.longValue()); + ssize_t sst = new ssize_t(VALUE); + assertEquals("Wrong ssize_t value", VALUE, sst.longValue()); + off_t ot = new off_t(VALUE); + assertEquals("Wrong off_t value", VALUE, ot.longValue()); + ot = new off_t(VALUE, true); + assertEquals("Wrong 64 bit off_t value", VALUE, ot.longValue()); + } + @Test public void testSysinfo() { Sysinfo info = new Sysinfo(); From 32c84e1d347453806cb5b9ff6d23c9ea3ac9c2e1 Mon Sep 17 00:00:00 2001 From: Daniel Widdis Date: Mon, 8 Jun 2020 15:55:46 -0700 Subject: [PATCH 3/9] Add more mappings supporting shared memory --- CHANGES.md | 2 +- .../src/com/sun/jna/platform/linux/ErrNo.java | 139 ++++++++++++++++ .../src/com/sun/jna/platform/linux/Fcntl.java | 64 ++++++++ .../src/com/sun/jna/platform/linux/LibC.java | 4 +- .../src/com/sun/jna/platform/linux/LibRT.java | 73 +++++++++ .../src/com/sun/jna/platform/linux/Mman.java | 77 +++++++++ .../com/sun/jna/platform/unix/LibCAPI.java | 150 ++++++++++++++++-- .../com/sun/jna/platform/unix/LibCUtil.java | 140 ++++++++++++++++ .../com/sun/jna/platform/linux/LibRTTest.java | 120 ++++++++++++++ 9 files changed, 755 insertions(+), 14 deletions(-) create mode 100644 contrib/platform/src/com/sun/jna/platform/linux/ErrNo.java create mode 100644 contrib/platform/src/com/sun/jna/platform/linux/Fcntl.java create mode 100644 contrib/platform/src/com/sun/jna/platform/linux/LibRT.java create mode 100644 contrib/platform/src/com/sun/jna/platform/linux/Mman.java create mode 100644 contrib/platform/src/com/sun/jna/platform/unix/LibCUtil.java create mode 100644 contrib/platform/test/com/sun/jna/platform/linux/LibRTTest.java diff --git a/CHANGES.md b/CHANGES.md index 6c0793f380..60bdf75096 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -17,7 +17,7 @@ Features * [#1194](https://github.com/java-native-access/jna/pull/1194): Add `GetConsoleScreenBufferInfo`, `ReadConsoleInput` and `WriteConsole` with associated structures to `c.s.j.p.win32.Wincon` - [@rednoah](https://github.com/rednoah). * [#1198](https://github.com/java-native-access/jna/pull/1198): Add `NetSessionEnum` to `c.s.j.p.win32.Netapi32` and `WTSEnumerateSessions`, `WTSQuerySessionInformation`, and `WTSFreeMemory` to `c.s.j.p.win32.Wtsapi32` - [@dbwiddis](https://github.com/dbwiddis). * [#1200](https://github.com/java-native-access/jna/pull/1200): Add mappings for `libudev` to `c.s.j.p.linux.Udev` - [@dbwiddis](https://github.com/dbwiddis). -* [#1202](https://github.com/java-native-access/jna/pull/1202): Add `size_t`, `ssize_t`, and `off_t` to `c.s.j.p.unix.LibCAPI` - [@dbwiddis](https://github.com/dbwiddis). +* [#1202](https://github.com/java-native-access/jna/pull/1202): Add mappings supporting shared memory `c.s.j.p.unix.LibCAPI` including `size_t`, `ssize_t`, and `off_t` types and `mmap()`, `munmap()`, `msync()`, `ftruncate()`, and `close()` methods, `c.s.j.p.unix.LibCUtil` exposing `OFF_T_SIZE` and mapping `mmap()`, `c.s.j.p.linux.LibRT` methods `shm_open()` and `shm_unlink()` - [@dbwiddis](https://github.com/dbwiddis). Bug Fixes --------- diff --git a/contrib/platform/src/com/sun/jna/platform/linux/ErrNo.java b/contrib/platform/src/com/sun/jna/platform/linux/ErrNo.java new file mode 100644 index 0000000000..7469ad37b3 --- /dev/null +++ b/contrib/platform/src/com/sun/jna/platform/linux/ErrNo.java @@ -0,0 +1,139 @@ +package com.sun.jna.platform.linux; + +import com.sun.jna.Native; + +/** + * System error codes set in {@code errno} and retrieved by + * {@link Native#getLastError()} + */ +public interface ErrNo { + int EPERM = 1; // Operation not permitted + int ENOENT = 2; // No such file or directory + int ESRCH = 3; // No such process + int EINTR = 4; // Interrupted system call + int EIO = 5; // I/O error + int ENXIO = 6; // No such device or address + int E2BIG = 7; // Argument list too long + int ENOEXEC = 8; // Exec format error + int EBADF = 9; // Bad file number + int ECHILD = 10; // No child processes + int EAGAIN = 11; // Try again + int ENOMEM = 12; // Out of memory + int EACCES = 13; // Permission denied + int EFAULT = 14; // Bad address + int ENOTBLK = 15; // Block device required + int EBUSY = 16; // Device or resource busy + int EEXIST = 17; // File exists + int EXDEV = 18; // Cross-device link + int ENODEV = 19; // No such device + int ENOTDIR = 20; // Not a directory + int EISDIR = 21; // Is a directory + int EINVAL = 22; // Invalid argument + int ENFILE = 23; // File table overflow + int EMFILE = 24; // Too many open files + int ENOTTY = 25; // Not a typewriter + int ETXTBSY = 26; // Text file busy + int EFBIG = 27; // File too large + int ENOSPC = 28; // No space left on device + int ESPIPE = 29; // Illegal seek + int EROFS = 30; // Read-only file system + int EMLINK = 31; // Too many links + int EPIPE = 32; // Broken pipe + int EDOM = 33; // Math argument out of domain of func + int ERANGE = 34; // Math result not representable + int EDEADLK = 35; // Resource deadlock would occur + int ENAMETOOLONG = 36; // File name too long + int ENOLCK = 37; // No record locks available + int ENOSYS = 38; // Function not implemented + int ENOTEMPTY = 39; // Directory not empty + int ELOOP = 40; // Too many symbolic links encountered + int ENOMSG = 42; // No message of desired type + int EIDRM = 43; // Identifier removed + int ECHRNG = 44; // Channel number out of range + int EL2NSYNC = 45; // Level 2 not synchronized + int EL3HLT = 46; // Level 3 halted + int EL3RST = 47; // Level 3 reset + int ELNRNG = 48; // Link number out of range + int EUNATCH = 49; // Protocol driver not attached + int ENOCSI = 50; // No CSI structure available + int EL2HLT = 51; // Level 2 halted + int EBADE = 52; // Invalid exchange + int EBADR = 53; // Invalid request descriptor + int EXFULL = 54; // Exchange full + int ENOANO = 55; // No anode + int EBADRQC = 56; // Invalid request code + int EBADSLT = 57; // Invalid slot + int EBFONT = 59; // Bad font file format + int ENOSTR = 60; // Device not a stream + int ENODATA = 61; // No data available + int ETIME = 62; // Timer expired + int ENOSR = 63; // Out of streams resources + int ENONET = 64; // Machine is not on the network + int ENOPKG = 65; // Package not installed + int EREMOTE = 66; // Object is remote + int ENOLINK = 67; // Link has been severed + int EADV = 68; // Advertise error + int ESRMNT = 69; // Srmount error + int ECOMM = 70; // Communication error on send + int EPROTO = 71; // Protocol error + int EMULTIHOP = 72; // Multihop attempted + int EDOTDOT = 73; // RFS specific error + int EBADMSG = 74; // Not a data message + int EOVERFLOW = 75; // Value too large for defined data type + int ENOTUNIQ = 76; // Name not unique on network + int EBADFD = 77; // File descriptor in bad state + int EREMCHG = 78; // Remote address changed + int ELIBACC = 79; // Can not access a needed shared library + int ELIBBAD = 80; // Accessing a corrupted shared library + int ELIBSCN = 81; // .lib section in a.out corrupted + int ELIBMAX = 82; // Attempting to link in too many shared libraries + int ELIBEXEC = 83; // Cannot exec a shared library directly + int EILSEQ = 84; // Illegal byte sequence + int ERESTART = 85; // Interrupted system call should be restarted + int ESTRPIPE = 86; // Streams pipe error + int EUSERS = 87; // Too many users + int ENOTSOCK = 88; // Socket operation on non-socket + int EDESTADDRREQ = 89; // Destination address required + int EMSGSIZE = 90; // Message too long + int EPROTOTYPE = 91; // Protocol wrong type for socket + int ENOPROTOOPT = 92; // Protocol not available + int EPROTONOSUPPORT = 93; // Protocol not supported + int ESOCKTNOSUPPORT = 94; // Socket type not supported + int EOPNOTSUPP = 95; // Operation not supported on transport endpoint + int EPFNOSUPPORT = 96; // Protocol family not supported + int EAFNOSUPPORT = 97; // Address family not supported by protocol + int EADDRINUSE = 98; // Address already in use + int EADDRNOTAVAIL = 99; // Cannot assign requested address + int ENETDOWN = 100; // Network is down + int ENETUNREACH = 101; // Network is unreachable + int ENETRESET = 102; // Network dropped connection because of reset + int ECONNABORTED = 103; // Software caused connection abort + int ECONNRESET = 104; // Connection reset by peer + int ENOBUFS = 105; // No buffer space available + int EISCONN = 106; // Transport endpoint is already connected + int ENOTCONN = 107; // Transport endpoint is not connected + int ESHUTDOWN = 108; // Cannot send after transport endpoint shutdown + int ETOOMANYREFS = 109; // Too many references: cannot splice + int ETIMEDOUT = 110; // Connection timed out + int ECONNREFUSED = 111; // Connection refused + int EHOSTDOWN = 112; // Host is down + int EHOSTUNREACH = 113; // No route to host + int EALREADY = 114; // Operation already in progress + int EINPROGRESS = 115; // Operation now in progress + int ESTALE = 116; // Stale NFS file handle + int EUCLEAN = 117; // Structure needs cleaning + int ENOTNAM = 118; // Not a XENIX named type file + int ENAVAIL = 119; // No XENIX semaphores available + int EISNAM = 120; // Is a named type file + int EREMOTEIO = 121; // Remote I/O error + int EDQUOT = 122; // Quota exceeded + int ENOMEDIUM = 123; // No medium found + int EMEDIUMTYPE = 124; // Wrong medium type + int ECANCELED = 125; // Operation Canceled + int ENOKEY = 126; // Required key not available + int EKEYEXPIRED = 127; // Key has expired + int EKEYREVOKED = 128; // Key has been revoked + int EKEYREJECTED = 129; // Key was rejected by service + int EOWNERDEAD = 130; // Owner died + int ENOTRECOVERABLE = 131; // State not recoverable +} \ No newline at end of file diff --git a/contrib/platform/src/com/sun/jna/platform/linux/Fcntl.java b/contrib/platform/src/com/sun/jna/platform/linux/Fcntl.java new file mode 100644 index 0000000000..c5afe0dd5a --- /dev/null +++ b/contrib/platform/src/com/sun/jna/platform/linux/Fcntl.java @@ -0,0 +1,64 @@ +/* Copyright (c) 2020 Daniel Widdis, All Rights Reserved + * + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +package com.sun.jna.platform.linux; + +/** + * POSIX Standard: 6.5 File Control Operations from {@code fcntl.h} + */ +public interface Fcntl { + /* + * File access modes for `open' and `fcntl' + */ + int O_RDONLY = 00; // Open read-only. + int O_WRONLY = 01; // Open write-only. + int O_RDWR = 02; // Open read/write. + + /* + * Bits OR'd into the second argument to open. Note these are defined + * differently on linux than unix fcntl header + */ + int O_CREAT = 0100; // Create file if it doesn't exist. + int O_EXCL = 0200; // Fail if file already exists. + int O_TRUNC = 01000; // Truncate file to zero length. + + /* Protection bits. */ + int S_IRUSR = 00400; // Read by owner. + int S_IWUSR = 00200; // Write by owner. + int S_IXUSR = 00100; // Execute by owner. + int S_IRWXU = S_IRUSR | S_IWUSR | S_IXUSR; + + int S_IRGRP = 00040; // Read by group. + int S_IWGRP = 00020; // Write by group. + int S_IXGRP = 00010; // Execute by group. + int S_IRWXG = S_IRGRP | S_IWGRP | S_IXGRP; + + int S_IROTH = 00004; // Read by others. + int S_IWOTH = 00002; // Write by others. + int S_IXOTH = 00001; // Execute by others. + int S_IRWXO = S_IROTH | S_IWOTH | S_IXOTH; + + int S_ISUID = 04000; // set-user-ID bit + int S_ISGID = 02000; // set-group-ID bit (see inode(7)). + int S_ISVTX = 01000; // sticky bit (see inode(7)). +} diff --git a/contrib/platform/src/com/sun/jna/platform/linux/LibC.java b/contrib/platform/src/com/sun/jna/platform/linux/LibC.java index ae21e0c9c5..c5e2860bba 100644 --- a/contrib/platform/src/com/sun/jna/platform/linux/LibC.java +++ b/contrib/platform/src/com/sun/jna/platform/linux/LibC.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2017 Daniel Widdis, All Rights Reserved +/* Copyright (c) 2017,2020 Daniel Widdis, All Rights Reserved * * The contents of this file is dual-licensed under 2 * alternative Open Source/Free licenses: LGPL 2.1 or later and @@ -38,7 +38,7 @@ /** * LibC structures and functions unique to Linux */ -public interface LibC extends LibCAPI, Library { +public interface LibC extends LibCAPI, ErrNo, Fcntl, Mman, Library { String NAME = "c"; LibC INSTANCE = Native.load(NAME, LibC.class); diff --git a/contrib/platform/src/com/sun/jna/platform/linux/LibRT.java b/contrib/platform/src/com/sun/jna/platform/linux/LibRT.java new file mode 100644 index 0000000000..0a87da670f --- /dev/null +++ b/contrib/platform/src/com/sun/jna/platform/linux/LibRT.java @@ -0,0 +1,73 @@ +/* Copyright (c) 2020 Daniel Widdis, All Rights Reserved + * + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +package com.sun.jna.platform.linux; + +import com.sun.jna.Library; +import com.sun.jna.Native; + +/** + * POSIX.1b Realtime Extensions library (librt). Functions in this library + * provide most of the interfaces specified by the POSIX.1b Realtime Extension. + */ +public interface LibRT extends Library { + + LibRT INSTANCE = Native.load("rt", LibRT.class); + + /** + * Creates and opens a new, or opens an existing, POSIX shared memory object. A + * POSIX shared memory object is in effect a handle which can be used by + * unrelated processes to {@code mmap()} the same region of shared memory. + * + * @param name + * The shared memory object to be created or opened. For portable + * use, a shared memory object should be identified by a name of the + * form {@code /somename} that is, a null-terminated string of up to + * {@code NAME_MAX} (i.e., 255) characters consisting of an initial + * slash, followed by one or more characters, none of which are + * slashes. + * @param oflag + * A bit mask created by ORing together exactly one of + * {@code O_RDONLY} or {@code O_RDWR} and any of the other flags + * {@code O_CREAT}, {@code O_EXCL}, or {@code O_TRUNC}. + * @param mode + * When {@code oflag} includes {@code O_CREAT}, the object's + * permission bits are set according to the low-order 9 bits of mode, + * except that those bits set in the process file mode creation mask + * (see {@code umask(2)}) are cleared for the new object. + * @return On success, returns a file descriptor (a nonnegative integer). On + * failure, returns -1. On failure, {@code errno} is set to indicate the + * cause of the error. + */ + int shm_open(String name, int oflag, int mode); + + /** + * Removes an object previously created by {@link #shm_open}. + * + * @param name + * The shared memory object to be unlinked. + * @return returns 0 on success, or -1 on error. On failure, {@code errno} is + * set to indicate the cause of the error. + */ + int shm_unlink(String name); +} diff --git a/contrib/platform/src/com/sun/jna/platform/linux/Mman.java b/contrib/platform/src/com/sun/jna/platform/linux/Mman.java new file mode 100644 index 0000000000..f035e18503 --- /dev/null +++ b/contrib/platform/src/com/sun/jna/platform/linux/Mman.java @@ -0,0 +1,77 @@ +/* Copyright (c) 2020 Daniel Widdis, All Rights Reserved + * + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +package com.sun.jna.platform.linux; + +import com.sun.jna.Pointer; + +/** + * Definitions for POSIX memory map interface from {@code mman.h} + */ +public interface Mman { + /* + * Protections are chosen from these bits, OR'd together. The implementation + * does not necessarily support PROT_EXEC or PROT_WRITE without PROT_READ. The + * only guarantees are that no writing will be allowed without PROT_WRITE and no + * access will be allowed for PROT_NONE. + */ + int PROT_READ = 0x1; // Page can be read. + int PROT_WRITE = 0x2; // Page can be written. + int PROT_EXEC = 0x4; // Page can be executed. + int PROT_NONE = 0x0; // Page can not be accessed. + int PROT_GROWSDOWN = 0x01000000; // Extend change to start of growsdown vma (mprotect only). + int PROT_GROWSUP = 0x02000000; // Extend change to start of growsup vma (mprotect only). + + /* Sharing types (must choose one and only one of these). */ + int MAP_SHARED = 0x01; // Share changes. + int MAP_PRIVATE = 0x02; // Changes are private. + int MAP_SHARED_VALIDATE = 0x03; // share + validate extension flags + int MAP_TYPE = 0x0f; // Mask for type of mapping + + /* Other flags. */ + int MAP_FILE = 0; // Compatibility flag. Ignored. + int MAP_FIXED = 0x10; // Interpret addr exactly. + int MAP_ANONYMOUS = 0x20; // Don't use a file. + int MAP_ANON = MAP_ANONYMOUS; + int MAP_32BIT = 0x40; // Only give out 32-bit addresses. + + /* These are Linux-specific. */ + int MAP_GROWSDOWN = 0x00100; // Stack-like segment. + int MAP_DENYWRITE = 0x00800; // ETXTBSY + int MAP_EXECUTABLE = 0x01000; // Mark it as an executable. + int MAP_LOCKED = 0x02000; // Lock the mapping. + int MAP_NORESERVE = 0x04000; // Don't check for reservations. + int MAP_POPULATE = 0x08000; // Populate (prefault) pagetables. + int MAP_NONBLOCK = 0x10000; // Do not block on IO. + int MAP_STACK = 0x20000; // Allocation is for a stack. + int MAP_HUGETLB = 0x40000; // create a huge page mapping + int MAP_SYNC = 0x80000; // perform synchronous page faults for the mapping + int MAP_FIXED_NOREPLACE = 0x100000; // MAP_FIXED which doesn't unmap underlying mapping + + Pointer MAP_FAILED = new Pointer(-1); // ((void *)-1) + + /* Flags for msync. */ + int MS_ASYNC = 1; + int MS_SYNC = 2; + int MS_INVALIDATE = 4; +} diff --git a/contrib/platform/src/com/sun/jna/platform/unix/LibCAPI.java b/contrib/platform/src/com/sun/jna/platform/unix/LibCAPI.java index 5808a05287..ba202fc04b 100644 --- a/contrib/platform/src/com/sun/jna/platform/unix/LibCAPI.java +++ b/contrib/platform/src/com/sun/jna/platform/unix/LibCAPI.java @@ -25,6 +25,7 @@ import com.sun.jna.IntegerType; import com.sun.jna.Native; +import com.sun.jna.Pointer; /** * Note: we are using this "intermediate" API in order to allow @@ -73,12 +74,9 @@ public ssize_t(long value) { * This is a signed integer type used to represent file sizes. *

* Authors of portable applications should be aware that on 32-bit operating - * systems, the bit width of this type is dependent on compile-time options in - * the end-user's C library. If the library is compiled with - * {@code _FILE_OFFSET_BITS == 64} this type is 64-bit. - *

- * The parameter {@code ilp32OffBig} permits this type to be defined as 64-bit - * on a 32-bit operating system. + * systems, the bit width of this type may be dependent on compile-time options + * in the end-user's library. The parameter {@code ilp32OffBig} permits this + * type to be defined as 64-bit on a 32-bit operating system. * * @see IEEE @@ -100,8 +98,8 @@ public off_t() { } /** - * Create a new {@code off_t} using the default bit width or optionally 64-bit - * width. + * Create a new {@code off_t} using the default bit width or 64-bit if + * specified. * * @param ilp32OffBig * If {@code true}, use 64-bit width. @@ -121,8 +119,8 @@ public off_t(long value) { } /** - * Create a new {@code off_t} using the default bit width or optionally 64-bit - * width. + * Create a new {@code off_t} using the default bit width or 64-bit if + * specified. * * @param value * The value to set. @@ -130,7 +128,7 @@ public off_t(long value) { * If {@code true}, use 64-bit width. */ public off_t(long value, boolean ilp32OffBig) { - super(ilp32OffBig ? 8 : Native.LONG_SIZE, value); + super(ilp32OffBig ? 8 : LibCUtil.OFF_T_SIZE, value); } } @@ -202,4 +200,134 @@ public off_t(long value, boolean ilp32OffBig) { * @see getloadavg(3) */ int getloadavg(double[] loadavg, int nelem); + + /** + * Closes a file descriptor, so that it no longer refers to any file and may be + * reused. Any record locks held on the file it was associated with, and owned + * by the process, are removed (regardless of the file descriptor that was used + * to obtain the lock). + *

+ * If {@code fd} is the last file descriptor referring to the underlying open + * file description, the resources associated with the open file description are + * freed; if the file descriptor was the last reference to a file which has been + * removed using {@code unlink}, the file is deleted. + * + * @param fd + * a file descriptor + * @return returns zero on success. On error, -1 is returned, and {@code errno} + * is set appropriately. + *

+ * {@code close()} should not be retried after an error. + */ + int close(int fd); + + /** + * Causes the regular file referenced by {@code fd} to be truncated to a size of + * precisely {@code length} bytes. + *

+ * If the file previously was larger than this size, the extra data is lost. If + * the file previously was shorter, it is extended, and the extended part reads + * as null bytes ('\0'). + *

+ * The file must be open for writing + * + * @param fd + * a file descriptor + * @param length + * the number of bytes to truncate or extend the file to + * @return On success, zero is returned. On error, -1 is returned, and + * {@code errno} is set appropriately. + */ + int ftruncate(int fd, off_t length); + + /** + * Creates a new mapping in the virtual address space of the calling process. + * + * @param addr + * The starting address for the new mapping. + *

+ * If {@code addr} is NULL, then the kernel chooses the + * (page-aligned) address at which to create the mapping; this is the + * most portable method of creating a new mapping. If {@code addr} is + * not NULL, then the kernel takes it as a hint about where to place + * the mapping; on Linux, the kernel will pick a nearby page boundary + * (but always above or equal to the value specified by + * {@code /proc/sys/vm/mmap_min_addr}) and attempt to create the + * mapping there. If another mapping already exists there, the kernel + * picks a new address that may or may not depend on the hint. The + * address of the new mapping is returned as the result of the call. + * @param length + * Specifies the length of the mapping (which must be greater than + * 0). + * @param prot + * describes the desired memory protection of the mapping (and must + * not conflict with the open mode of the file). It is either + * {@code PROT_NONE} or the bitwise OR of one or more of + * {@code PROT_READ}, {@code PROT_WRITE}, or {@code PROT_EXEC}. + * @param flags + * determines whether updates to the mapping are visible to other + * processes mapping the same region, and whether updates are carried + * through to the underlying file. This behavior is determined by + * including exactly one of {@code MAP_SHARED}, + * {@code MAP_SHARED_VALIDATE}, or {@code MAP_PRIVATE}. In addition, + * 0 or more additional flags can be ORed in {@code flags}. + * @param fd + * The file descriptor for the object to be mapped. After the + * {@code mmap()} call has returned, the file descriptor can be + * closed immediately without invalidating the mapping. + * @param offset + * The contents of a file mapping (as opposed to an anonymous + * mapping), are initialized using {@code length} bytes starting at + * offset {@code offset} in the file (or other object) referred to by + * the file descriptor, {@code fd}. {@code offset} must be a multiple + * of the page size as returned by {@code sysconf(_SC_PAGE_SIZE)}. + * @return On success, returns a pointer to the mapped area. On error, the value + * {@code MAP_FAILED} (that is, (void *) -1) is returned, and + * {@code errno} is set to indicate the cause of the error. + */ + Pointer mmap(Pointer addr, size_t length, int prot, int flags, int fd, off_t offset); + + /** + * Flushes changes made to the in-core copy of a file that was mapped into + * memory using {@link LibCAPI#mmap(Pointer, size_t, int, int, int, off_t)} back + * to the filesystem. Without use of this call, there is no guarantee that + * changes are written back before {@link #munmap(Pointer, size_t)} is called. + * To be more precise, the part of the file that corresponds to the memory area + * starting at {@code addr} and having length {@code length} is updated. + * + * @param addr + * The start of the memory area to sync to the filesystem. + * @param length + * The length of the memory area to sync to the filesystem. + * @param flags + * The flags argument should specify exactly one of {@code MS_ASYNC} + * and {@code MS_SYNC}, and may additionally include the + * {@code MS_INVALIDATE} bit. + * @return On success, zero is returned. On error, -1 is returned, and + * {@code errno} is set appropriately. + */ + int msync(Pointer addr, size_t length, int flags); + + /** + * Deletes the mappings for the specified address range, and causes further + * references to addresses within the range to generate invalid memory + * references. The region is also automatically unmapped when the process is + * terminated. On the other hand, closing the file descriptor does not unmap the + * region. + *

+ * It is not an error if the indicated range does not contain any mapped pages. + * + * @param addr + * The base address from which to delete mappings. The address addr + * must be a multiple of the page size (but length need not be). + * @param length + * The length from the base address to delete mappings. All pages + * containing a part of the indicated range are unmapped, and + * subsequent references to these pages will generate + * {@code SIGSEGV}. + * @return On success, returns 0. On failure, it returns -1, and {@code errno} + * is set to indicate the cause of the error (probably to + * {@code EINVAL}). + */ + int munmap(Pointer addr, size_t length); } diff --git a/contrib/platform/src/com/sun/jna/platform/unix/LibCUtil.java b/contrib/platform/src/com/sun/jna/platform/unix/LibCUtil.java new file mode 100644 index 0000000000..39e616f4e0 --- /dev/null +++ b/contrib/platform/src/com/sun/jna/platform/unix/LibCUtil.java @@ -0,0 +1,140 @@ +/* Copyright (c) 2020 Daniel Widdis, All Rights Reserved + * + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +package com.sun.jna.platform.unix; + +import com.sun.jna.Function; +import com.sun.jna.Native; +import com.sun.jna.NativeLibrary; +import com.sun.jna.Pointer; + +/** + * Utility class supporting variable-width types in the C Library + */ +public class LibCUtil { + + private static final NativeLibrary LIBC = NativeLibrary.getInstance("c"); + + /** Size of a native off_t type, in bytes. */ + public static final int OFF_T_SIZE; + static { + // Observations shows, that without LFS, on linux, solaris, aix, mac OS and + // windows (apart from cygwin32) sizeof(off_t) == sizeof(long) + int size = Native.LONG_SIZE; + // On 64-bit, off_t is 64 bit, otherwise test compiler flags that would create + // 64-bit versions of off_t functions + if (size < 8) { + try { + LIBC.getFunction("mmap64", Function.THROW_LAST_ERROR); + // on 32-bit, mmap64 only exists when off_t is 64-bit + size = 8; + } catch (UnsatisfiedLinkError ex) { + } + } + OFF_T_SIZE = size; + } + + private static Function mmap; + static { + try { + mmap = LIBC.getFunction("mmap64", Function.THROW_LAST_ERROR); + } catch (UnsatisfiedLinkError ex) { + mmap = LIBC.getFunction("mmap", Function.THROW_LAST_ERROR); + } + } + + private LibCUtil() { + } + + /** + * Creates a new mapping in the virtual address space of the calling process. + * + * @param addr + * The starting address for the new mapping. + *

+ * If {@code addr} is NULL, then the kernel chooses the + * (page-aligned) address at which to create the mapping; this is the + * most portable method of creating a new mapping. If {@code addr} is + * not NULL, then the kernel takes it as a hint about where to place + * the mapping; on Linux, the kernel will pick a nearby page boundary + * (but always above or equal to the value specified by + * {@code /proc/sys/vm/mmap_min_addr}) and attempt to create the + * mapping there. If another mapping already exists there, the kernel + * picks a new address that may or may not depend on the hint. The + * address of the new mapping is returned as the result of the call. + * @param length + * Specifies the length of the mapping (which must be greater than + * 0). + * @param prot + * describes the desired memory protection of the mapping (and must + * not conflict with the open mode of the file). It is either + * {@code PROT_NONE} or the bitwise OR of one or more of + * {@code PROT_READ}, {@code PROT_WRITE}, or {@code PROT_EXEC}. + * @param flags + * determines whether updates to the mapping are visible to other + * processes mapping the same region, and whether updates are carried + * through to the underlying file. This behavior is determined by + * including exactly one of {@code MAP_SHARED}, + * {@code MAP_SHARED_VALIDATE}, or {@code MAP_PRIVATE}. In addition, + * 0 or more additional flags can be ORed in {@code flags}. + * @param fd + * The file descriptor for the object to be mapped. After the + * {@code mmap()} call has returned, the file descriptor can be + * closed immediately without invalidating the mapping. + * @param offset + * The contents of a file mapping (as opposed to an anonymous + * mapping), are initialized using {@code length} bytes starting at + * offset {@code offset} in the file (or other object) referred to by + * the file descriptor, {@code fd}. {@code offset} must be a multiple + * of the page size as returned by {@code sysconf(_SC_PAGE_SIZE)}. + * @return On success, returns a pointer to the mapped area. On error, the value + * {@code MAP_FAILED} (that is, (void *) -1) is returned, and + * {@code errno} is set to indicate the cause of the error. + */ + public static Pointer mmap(Pointer addr, long length, int prot, int flags, int fd, long offset) { + Object[] params = new Object[6]; + params[0] = addr; + if (Native.SIZE_T_SIZE == 4) { + require32Bit(length, "length"); + params[1] = (int) length; + } else { + params[1] = length; + } + params[2] = prot; + params[3] = flags; + params[4] = fd; + if (OFF_T_SIZE == 4) { + require32Bit(offset, "offset"); + params[5] = (int) offset; + } else { + params[5] = offset; + } + return mmap.invokePointer(params); + } + + public static void require32Bit(long val, String value) { + if (val > Integer.MAX_VALUE) { + throw new IllegalArgumentException(value + " exceeds 32bit"); + } + } +} diff --git a/contrib/platform/test/com/sun/jna/platform/linux/LibRTTest.java b/contrib/platform/test/com/sun/jna/platform/linux/LibRTTest.java new file mode 100644 index 0000000000..7743ef6b33 --- /dev/null +++ b/contrib/platform/test/com/sun/jna/platform/linux/LibRTTest.java @@ -0,0 +1,120 @@ +/* Copyright (c) 2020 Daniel Widdis, All Rights Reserved + * + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +package com.sun.jna.platform.linux; + +import static com.sun.jna.platform.linux.ErrNo.EEXIST; +import static com.sun.jna.platform.linux.Fcntl.O_CREAT; +import static com.sun.jna.platform.linux.Fcntl.O_EXCL; +import static com.sun.jna.platform.linux.Fcntl.O_RDWR; +import static com.sun.jna.platform.linux.Fcntl.S_IRWXU; +import static com.sun.jna.platform.linux.Mman.MAP_FAILED; +import static com.sun.jna.platform.linux.Mman.MAP_SHARED; +import static com.sun.jna.platform.linux.Mman.MS_SYNC; +import static com.sun.jna.platform.linux.Mman.PROT_READ; +import static com.sun.jna.platform.linux.Mman.PROT_WRITE; +import static org.junit.Assert.assertNotEquals; + +import java.io.File; +import java.io.IOException; + +import org.junit.Test; + +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.platform.unix.LibCAPI.off_t; +import com.sun.jna.platform.unix.LibCAPI.size_t; + +import junit.framework.TestCase; + +public class LibRTTest extends TestCase { + + public static LibC LIBC = LibC.INSTANCE; + public static LibRT LIBRT = LibRT.INSTANCE; + + @Test + public void testMmapToShm() throws IOException { + // Get a suitably random filename of the form "/somename" + File f = File.createTempFile("mmapToShm", "test"); + String share = "/" + f.getName(); + f.delete(); + // Get a file descriptor to the share. + int fd = LIBRT.shm_open(share, O_RDWR | O_CREAT | O_EXCL, S_IRWXU); + assertNotEquals("Failed to shm_open " + share + ". Error: " + Native.getLastError(), -1, fd); + try { + // Multiply by 4 to handle all possible encodings + size_t length = new size_t(4 * (share.length() + 1)); + off_t truncLen = new off_t(4 * (share.length() + 1)); + // Allocate memory to the share (fills with null bytes) + int ret = LIBC.ftruncate(fd, truncLen); + assertNotEquals("Failed to ftruncate. Error: " + Native.getLastError(), -1, ret); + // Map a pointer to the share. Offset must be a multiple of page size + Pointer p = LIBC.mmap(null, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, off_t.ZERO); + assertNotEquals("Failed mmap to new share. Error: " + Native.getLastError(), MAP_FAILED, p); + // We can now close the file descriptor + ret = LIBC.close(fd); + assertNotEquals("Failed to close file descriptor. Error: " + Native.getLastError(), -1, ret); + // Write some bytes to the share. The name is a suitable candidate + p.setString(0, share); + // Sync from memory to share + ret = LIBC.msync(p, length, MS_SYNC); + assertNotEquals("Failed msync. Error: " + Native.getLastError(), -1, ret); + // Unmap the share + ret = LIBC.munmap(p, length); + assertNotEquals("Failed munmap. Error: " + Native.getLastError(), -1, ret); + // p now points to invalid memory + p = null; + + // Get another file descriptor to the same share. + // Calling with both O_CREAT | O_EXCL should fail since the share already exists + fd = LIBRT.shm_open(share, O_RDWR | O_CREAT | O_EXCL, S_IRWXU); + assertEquals("Re-creating existing share should have failed", -1, fd); + assertEquals("Re-creating existing share errno should be EEXIST", EEXIST, Native.getLastError()); + // So let's not recreate it, instead get a file descriptor to the existing share + fd = LIBRT.shm_open(share, O_RDWR, S_IRWXU); + assertNotEquals("Failed to re-open " + share + ". Error: " + Native.getLastError(), -1, fd); + // Map another pointer to the share. + Pointer q = LIBC.mmap(null, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, off_t.ZERO); + assertNotEquals("Failed mmap to existing share. Error: " + Native.getLastError(), MAP_FAILED, q); + // Close the file descriptor + ret = LIBC.close(fd); + assertNotEquals("Failed to close file descriptor. Error: " + Native.getLastError(), -1, ret); + // Check that the bytes we wrote are still there + assertEquals("Bytes written to share don't match", share, q.getString(0)); + // Unmap the share + ret = LIBC.munmap(q, length); + assertNotEquals("Failed munmap. Error: " + Native.getLastError(), -1, ret); + // q now points to invalid memory + q = null; + + // Unlink the share + ret = LIBRT.shm_unlink(share); + assertNotEquals("Failed to shm_unlink " + share + ". Error: " + Native.getLastError(), -1, ret); + // Should be able to re-create now + fd = LIBRT.shm_open(share, O_RDWR | O_CREAT | O_EXCL, S_IRWXU); + assertNotEquals("Failed to reopen unlinked " + share + ". Error: " + Native.getLastError(), -1, fd); + } finally { + LIBRT.shm_unlink(share); + } + } +} From 813b4a7e0f12bd4b1c426f4a297c871e5a2dd185 Mon Sep 17 00:00:00 2001 From: Daniel Widdis Date: Mon, 8 Jun 2020 16:13:35 -0700 Subject: [PATCH 4/9] Copyright header, add test of util mmap --- .../src/com/sun/jna/platform/linux/ErrNo.java | 23 +++++++++++++++++++ .../com/sun/jna/platform/linux/LibRTTest.java | 10 ++++---- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/contrib/platform/src/com/sun/jna/platform/linux/ErrNo.java b/contrib/platform/src/com/sun/jna/platform/linux/ErrNo.java index 7469ad37b3..95b59fc233 100644 --- a/contrib/platform/src/com/sun/jna/platform/linux/ErrNo.java +++ b/contrib/platform/src/com/sun/jna/platform/linux/ErrNo.java @@ -1,3 +1,26 @@ +/* Copyright (c) 2020 Daniel Widdis, All Rights Reserved + * + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ package com.sun.jna.platform.linux; import com.sun.jna.Native; diff --git a/contrib/platform/test/com/sun/jna/platform/linux/LibRTTest.java b/contrib/platform/test/com/sun/jna/platform/linux/LibRTTest.java index 7743ef6b33..32800ba69c 100644 --- a/contrib/platform/test/com/sun/jna/platform/linux/LibRTTest.java +++ b/contrib/platform/test/com/sun/jna/platform/linux/LibRTTest.java @@ -44,6 +44,7 @@ import com.sun.jna.Pointer; import com.sun.jna.platform.unix.LibCAPI.off_t; import com.sun.jna.platform.unix.LibCAPI.size_t; +import com.sun.jna.platform.unix.LibCUtil; import junit.framework.TestCase; @@ -63,8 +64,9 @@ public void testMmapToShm() throws IOException { assertNotEquals("Failed to shm_open " + share + ". Error: " + Native.getLastError(), -1, fd); try { // Multiply by 4 to handle all possible encodings - size_t length = new size_t(4 * (share.length() + 1)); - off_t truncLen = new off_t(4 * (share.length() + 1)); + int bufLen = 4 * (share.length() + 1); + size_t length = new size_t(bufLen); + off_t truncLen = new off_t(bufLen); // Allocate memory to the share (fills with null bytes) int ret = LIBC.ftruncate(fd, truncLen); assertNotEquals("Failed to ftruncate. Error: " + Native.getLastError(), -1, ret); @@ -93,8 +95,8 @@ public void testMmapToShm() throws IOException { // So let's not recreate it, instead get a file descriptor to the existing share fd = LIBRT.shm_open(share, O_RDWR, S_IRWXU); assertNotEquals("Failed to re-open " + share + ". Error: " + Native.getLastError(), -1, fd); - // Map another pointer to the share. - Pointer q = LIBC.mmap(null, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, off_t.ZERO); + // Map another pointer to the share. Use the util version to test it + Pointer q = LibCUtil.mmap(null, bufLen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); assertNotEquals("Failed mmap to existing share. Error: " + Native.getLastError(), MAP_FAILED, q); // Close the file descriptor ret = LIBC.close(fd); From f964e9fb32e42183415e3369491fc23551847fae Mon Sep 17 00:00:00 2001 From: Daniel Widdis Date: Sun, 14 Jun 2020 15:28:59 -0700 Subject: [PATCH 5/9] Remove OFF_T_SIZE and map portable ftruncate --- CHANGES.md | 2 +- .../src/com/sun/jna/platform/linux/LibC.java | 4 +- .../com/sun/jna/platform/unix/LibCAPI.java | 74 ++---------------- .../com/sun/jna/platform/unix/LibCUtil.java | 77 +++++++++++++------ .../com/sun/jna/platform/linux/LibRTTest.java | 6 +- 5 files changed, 64 insertions(+), 99 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 60bdf75096..f6b8416693 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -17,7 +17,7 @@ Features * [#1194](https://github.com/java-native-access/jna/pull/1194): Add `GetConsoleScreenBufferInfo`, `ReadConsoleInput` and `WriteConsole` with associated structures to `c.s.j.p.win32.Wincon` - [@rednoah](https://github.com/rednoah). * [#1198](https://github.com/java-native-access/jna/pull/1198): Add `NetSessionEnum` to `c.s.j.p.win32.Netapi32` and `WTSEnumerateSessions`, `WTSQuerySessionInformation`, and `WTSFreeMemory` to `c.s.j.p.win32.Wtsapi32` - [@dbwiddis](https://github.com/dbwiddis). * [#1200](https://github.com/java-native-access/jna/pull/1200): Add mappings for `libudev` to `c.s.j.p.linux.Udev` - [@dbwiddis](https://github.com/dbwiddis). -* [#1202](https://github.com/java-native-access/jna/pull/1202): Add mappings supporting shared memory `c.s.j.p.unix.LibCAPI` including `size_t`, `ssize_t`, and `off_t` types and `mmap()`, `munmap()`, `msync()`, `ftruncate()`, and `close()` methods, `c.s.j.p.unix.LibCUtil` exposing `OFF_T_SIZE` and mapping `mmap()`, `c.s.j.p.linux.LibRT` methods `shm_open()` and `shm_unlink()` - [@dbwiddis](https://github.com/dbwiddis). +* [#1202](https://github.com/java-native-access/jna/pull/1202): Add mappings supporting shared memory including `c.s.j.p.unix.LibCAPI` types `size_t`, `ssize_t`, and `off_t`, `c.s.j.p.linux.LibC` methods `munmap()`, `msync()`, and `close()`, `c.s.j.p.unix.LibCUtil` mapping `mmap()` and `ftruncate()`, and `c.s.j.p.linux.LibRT` methods `shm_open()` and `shm_unlink()` - [@dbwiddis](https://github.com/dbwiddis). Bug Fixes --------- diff --git a/contrib/platform/src/com/sun/jna/platform/linux/LibC.java b/contrib/platform/src/com/sun/jna/platform/linux/LibC.java index c5e2860bba..ae21e0c9c5 100644 --- a/contrib/platform/src/com/sun/jna/platform/linux/LibC.java +++ b/contrib/platform/src/com/sun/jna/platform/linux/LibC.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2017,2020 Daniel Widdis, All Rights Reserved +/* Copyright (c) 2017 Daniel Widdis, All Rights Reserved * * The contents of this file is dual-licensed under 2 * alternative Open Source/Free licenses: LGPL 2.1 or later and @@ -38,7 +38,7 @@ /** * LibC structures and functions unique to Linux */ -public interface LibC extends LibCAPI, ErrNo, Fcntl, Mman, Library { +public interface LibC extends LibCAPI, Library { String NAME = "c"; LibC INSTANCE = Native.load(NAME, LibC.class); diff --git a/contrib/platform/src/com/sun/jna/platform/unix/LibCAPI.java b/contrib/platform/src/com/sun/jna/platform/unix/LibCAPI.java index ba202fc04b..23dce12c45 100644 --- a/contrib/platform/src/com/sun/jna/platform/unix/LibCAPI.java +++ b/contrib/platform/src/com/sun/jna/platform/unix/LibCAPI.java @@ -77,6 +77,9 @@ public ssize_t(long value) { * systems, the bit width of this type may be dependent on compile-time options * in the end-user's library. The parameter {@code ilp32OffBig} permits this * type to be defined as 64-bit on a 32-bit operating system. + *

+ * Portable versions of C library methods which ensure appropriate bit width for + * {@code off_t} arguments are defined in {@link LibCUtil}. * * @see IEEE @@ -86,7 +89,6 @@ public ssize_t(long value) { * Std 1003.1-2017 (POSIX v7) */ class off_t extends IntegerType { - public static final off_t ZERO = new off_t(); private static final long serialVersionUID = 1L; @@ -128,7 +130,7 @@ public off_t(long value) { * If {@code true}, use 64-bit width. */ public off_t(long value, boolean ilp32OffBig) { - super(ilp32OffBig ? 8 : LibCUtil.OFF_T_SIZE, value); + super(ilp32OffBig ? 8 : Native.LONG_SIZE, value); } } @@ -221,75 +223,9 @@ public off_t(long value, boolean ilp32OffBig) { */ int close(int fd); - /** - * Causes the regular file referenced by {@code fd} to be truncated to a size of - * precisely {@code length} bytes. - *

- * If the file previously was larger than this size, the extra data is lost. If - * the file previously was shorter, it is extended, and the extended part reads - * as null bytes ('\0'). - *

- * The file must be open for writing - * - * @param fd - * a file descriptor - * @param length - * the number of bytes to truncate or extend the file to - * @return On success, zero is returned. On error, -1 is returned, and - * {@code errno} is set appropriately. - */ - int ftruncate(int fd, off_t length); - - /** - * Creates a new mapping in the virtual address space of the calling process. - * - * @param addr - * The starting address for the new mapping. - *

- * If {@code addr} is NULL, then the kernel chooses the - * (page-aligned) address at which to create the mapping; this is the - * most portable method of creating a new mapping. If {@code addr} is - * not NULL, then the kernel takes it as a hint about where to place - * the mapping; on Linux, the kernel will pick a nearby page boundary - * (but always above or equal to the value specified by - * {@code /proc/sys/vm/mmap_min_addr}) and attempt to create the - * mapping there. If another mapping already exists there, the kernel - * picks a new address that may or may not depend on the hint. The - * address of the new mapping is returned as the result of the call. - * @param length - * Specifies the length of the mapping (which must be greater than - * 0). - * @param prot - * describes the desired memory protection of the mapping (and must - * not conflict with the open mode of the file). It is either - * {@code PROT_NONE} or the bitwise OR of one or more of - * {@code PROT_READ}, {@code PROT_WRITE}, or {@code PROT_EXEC}. - * @param flags - * determines whether updates to the mapping are visible to other - * processes mapping the same region, and whether updates are carried - * through to the underlying file. This behavior is determined by - * including exactly one of {@code MAP_SHARED}, - * {@code MAP_SHARED_VALIDATE}, or {@code MAP_PRIVATE}. In addition, - * 0 or more additional flags can be ORed in {@code flags}. - * @param fd - * The file descriptor for the object to be mapped. After the - * {@code mmap()} call has returned, the file descriptor can be - * closed immediately without invalidating the mapping. - * @param offset - * The contents of a file mapping (as opposed to an anonymous - * mapping), are initialized using {@code length} bytes starting at - * offset {@code offset} in the file (or other object) referred to by - * the file descriptor, {@code fd}. {@code offset} must be a multiple - * of the page size as returned by {@code sysconf(_SC_PAGE_SIZE)}. - * @return On success, returns a pointer to the mapped area. On error, the value - * {@code MAP_FAILED} (that is, (void *) -1) is returned, and - * {@code errno} is set to indicate the cause of the error. - */ - Pointer mmap(Pointer addr, size_t length, int prot, int flags, int fd, off_t offset); - /** * Flushes changes made to the in-core copy of a file that was mapped into - * memory using {@link LibCAPI#mmap(Pointer, size_t, int, int, int, off_t)} back + * memory using {@link LibCUtil#mmap(Pointer, long, int, int, int, long)} back * to the filesystem. Without use of this call, there is no guarantee that * changes are written back before {@link #munmap(Pointer, size_t)} is called. * To be more precise, the part of the file that corresponds to the memory area diff --git a/contrib/platform/src/com/sun/jna/platform/unix/LibCUtil.java b/contrib/platform/src/com/sun/jna/platform/unix/LibCUtil.java index 39e616f4e0..5a6a8af176 100644 --- a/contrib/platform/src/com/sun/jna/platform/unix/LibCUtil.java +++ b/contrib/platform/src/com/sun/jna/platform/unix/LibCUtil.java @@ -35,32 +35,23 @@ public class LibCUtil { private static final NativeLibrary LIBC = NativeLibrary.getInstance("c"); - /** Size of a native off_t type, in bytes. */ - public static final int OFF_T_SIZE; - static { - // Observations shows, that without LFS, on linux, solaris, aix, mac OS and - // windows (apart from cygwin32) sizeof(off_t) == sizeof(long) - int size = Native.LONG_SIZE; - // On 64-bit, off_t is 64 bit, otherwise test compiler flags that would create - // 64-bit versions of off_t functions - if (size < 8) { - try { - LIBC.getFunction("mmap64", Function.THROW_LAST_ERROR); - // on 32-bit, mmap64 only exists when off_t is 64-bit - size = 8; - } catch (UnsatisfiedLinkError ex) { - } - } - OFF_T_SIZE = size; - } - - private static Function mmap; + private static Function mmap = null; + private static boolean mmap64 = false; + private static Function ftruncate = null; + private static boolean ftruncate64 = false; static { try { mmap = LIBC.getFunction("mmap64", Function.THROW_LAST_ERROR); + mmap64 = true; } catch (UnsatisfiedLinkError ex) { mmap = LIBC.getFunction("mmap", Function.THROW_LAST_ERROR); } + try { + ftruncate = LIBC.getFunction("ftruncate64", Function.THROW_LAST_ERROR); + ftruncate64 = true; + } catch (UnsatisfiedLinkError ex) { + ftruncate = LIBC.getFunction("ftruncate", Function.THROW_LAST_ERROR); + } } private LibCUtil() { @@ -123,15 +114,55 @@ public static Pointer mmap(Pointer addr, long length, int prot, int flags, int f params[2] = prot; params[3] = flags; params[4] = fd; - if (OFF_T_SIZE == 4) { + if (mmap64 || Native.LONG_SIZE > 4) { + params[5] = offset; + } else { require32Bit(offset, "offset"); params[5] = (int) offset; - } else { - params[5] = offset; } return mmap.invokePointer(params); } + /** + * Causes the regular file referenced by {@code fd} to be truncated to a size of + * precisely {@code length} bytes. + *

+ * If the file previously was larger than this size, the extra data is lost. If + * the file previously was shorter, it is extended, and the extended part reads + * as null bytes ('\0'). + *

+ * The file must be open for writing + * + * @param fd + * a file descriptor + * @param length + * the number of bytes to truncate or extend the file to + * @return On success, zero is returned. On error, -1 is returned, and + * {@code errno} is set appropriately. + */ + public static int ftruncate(int fd, long length) { + Object[] params = new Object[2]; + params[0] = fd; + if (ftruncate64 || Native.LONG_SIZE > 4) { + params[1] = length; + } else { + require32Bit(length, "length"); + params[1] = (int) length; + } + return ftruncate.invokeInt(params); + } + + /** + * Test that a value is 32-bit, throwing a custom exception otherwise + * + * @param val + * The value to test + * @param value + * The name of the value, to be inserted in the exception message if + * not 32-bit + * @throws IllegalArgumentException + * if {@code val} is not 32-bit + */ public static void require32Bit(long val, String value) { if (val > Integer.MAX_VALUE) { throw new IllegalArgumentException(value + " exceeds 32bit"); diff --git a/contrib/platform/test/com/sun/jna/platform/linux/LibRTTest.java b/contrib/platform/test/com/sun/jna/platform/linux/LibRTTest.java index 32800ba69c..73a0ce3b7a 100644 --- a/contrib/platform/test/com/sun/jna/platform/linux/LibRTTest.java +++ b/contrib/platform/test/com/sun/jna/platform/linux/LibRTTest.java @@ -42,7 +42,6 @@ import com.sun.jna.Native; import com.sun.jna.Pointer; -import com.sun.jna.platform.unix.LibCAPI.off_t; import com.sun.jna.platform.unix.LibCAPI.size_t; import com.sun.jna.platform.unix.LibCUtil; @@ -66,12 +65,11 @@ public void testMmapToShm() throws IOException { // Multiply by 4 to handle all possible encodings int bufLen = 4 * (share.length() + 1); size_t length = new size_t(bufLen); - off_t truncLen = new off_t(bufLen); // Allocate memory to the share (fills with null bytes) - int ret = LIBC.ftruncate(fd, truncLen); + int ret = LibCUtil.ftruncate(fd, bufLen); assertNotEquals("Failed to ftruncate. Error: " + Native.getLastError(), -1, ret); // Map a pointer to the share. Offset must be a multiple of page size - Pointer p = LIBC.mmap(null, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, off_t.ZERO); + Pointer p = LibCUtil.mmap(null, bufLen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); assertNotEquals("Failed mmap to new share. Error: " + Native.getLastError(), MAP_FAILED, p); // We can now close the file descriptor ret = LIBC.close(fd); From 010267ca3570ef34ed24fd98d65edbb68a588185 Mon Sep 17 00:00:00 2001 From: Daniel Widdis Date: Tue, 16 Jun 2020 12:30:53 -0700 Subject: [PATCH 6/9] Remove off_t mapping --- CHANGES.md | 2 +- .../com/sun/jna/platform/unix/LibCAPI.java | 64 ------------------- .../com/sun/jna/platform/unix/LibCUtil.java | 5 +- 3 files changed, 5 insertions(+), 66 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index f6b8416693..fdf8797909 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -17,7 +17,7 @@ Features * [#1194](https://github.com/java-native-access/jna/pull/1194): Add `GetConsoleScreenBufferInfo`, `ReadConsoleInput` and `WriteConsole` with associated structures to `c.s.j.p.win32.Wincon` - [@rednoah](https://github.com/rednoah). * [#1198](https://github.com/java-native-access/jna/pull/1198): Add `NetSessionEnum` to `c.s.j.p.win32.Netapi32` and `WTSEnumerateSessions`, `WTSQuerySessionInformation`, and `WTSFreeMemory` to `c.s.j.p.win32.Wtsapi32` - [@dbwiddis](https://github.com/dbwiddis). * [#1200](https://github.com/java-native-access/jna/pull/1200): Add mappings for `libudev` to `c.s.j.p.linux.Udev` - [@dbwiddis](https://github.com/dbwiddis). -* [#1202](https://github.com/java-native-access/jna/pull/1202): Add mappings supporting shared memory including `c.s.j.p.unix.LibCAPI` types `size_t`, `ssize_t`, and `off_t`, `c.s.j.p.linux.LibC` methods `munmap()`, `msync()`, and `close()`, `c.s.j.p.unix.LibCUtil` mapping `mmap()` and `ftruncate()`, and `c.s.j.p.linux.LibRT` methods `shm_open()` and `shm_unlink()` - [@dbwiddis](https://github.com/dbwiddis). +* [#1202](https://github.com/java-native-access/jna/pull/1202): Add mappings supporting shared memory including `c.s.j.p.unix.LibCAPI` types `size_t` and `ssize_t`, `c.s.j.p.linux.LibC` methods `munmap()`, `msync()`, and `close()`, `c.s.j.p.unix.LibCUtil` mapping `mmap()` and `ftruncate()`, and `c.s.j.p.linux.LibRT` methods `shm_open()` and `shm_unlink()` - [@dbwiddis](https://github.com/dbwiddis). Bug Fixes --------- diff --git a/contrib/platform/src/com/sun/jna/platform/unix/LibCAPI.java b/contrib/platform/src/com/sun/jna/platform/unix/LibCAPI.java index 23dce12c45..c2a9b9485e 100644 --- a/contrib/platform/src/com/sun/jna/platform/unix/LibCAPI.java +++ b/contrib/platform/src/com/sun/jna/platform/unix/LibCAPI.java @@ -70,70 +70,6 @@ public ssize_t(long value) { } } - /** - * This is a signed integer type used to represent file sizes. - *

- * Authors of portable applications should be aware that on 32-bit operating - * systems, the bit width of this type may be dependent on compile-time options - * in the end-user's library. The parameter {@code ilp32OffBig} permits this - * type to be defined as 64-bit on a 32-bit operating system. - *

- * Portable versions of C library methods which ensure appropriate bit width for - * {@code off_t} arguments are defined in {@link LibCUtil}. - * - * @see IEEE - * Std 1003.1, 2004 (POSIXv6) - * @see IEEE - * Std 1003.1-2017 (POSIX v7) - */ - class off_t extends IntegerType { - - private static final long serialVersionUID = 1L; - - /** - * Create a new {@code off_t} using the default bit width. - */ - public off_t() { - this(0, false); - } - - /** - * Create a new {@code off_t} using the default bit width or 64-bit if - * specified. - * - * @param ilp32OffBig - * If {@code true}, use 64-bit width. - */ - public off_t(boolean ilp32OffBig) { - this(0, ilp32OffBig); - } - - /** - * Create a new {@code off_t} using the default bit width. - * - * @param value - * The value to set. - */ - public off_t(long value) { - this(value, false); - } - - /** - * Create a new {@code off_t} using the default bit width or 64-bit if - * specified. - * - * @param value - * The value to set. - * @param ilp32OffBig - * If {@code true}, use 64-bit width. - */ - public off_t(long value, boolean ilp32OffBig) { - super(ilp32OffBig ? 8 : Native.LONG_SIZE, value); - } - } - // see man(2) get/set uid/gid int getuid(); int geteuid(); diff --git a/contrib/platform/src/com/sun/jna/platform/unix/LibCUtil.java b/contrib/platform/src/com/sun/jna/platform/unix/LibCUtil.java index 5a6a8af176..36499834df 100644 --- a/contrib/platform/src/com/sun/jna/platform/unix/LibCUtil.java +++ b/contrib/platform/src/com/sun/jna/platform/unix/LibCUtil.java @@ -29,7 +29,10 @@ import com.sun.jna.Pointer; /** - * Utility class supporting variable-width types in the C Library + * Utility class supporting variable-width types in the C Library. Portable code + * should avoid using {@code off_t} because it is dependent upon features + * selected at compile-time. This class provides helper methods that safely + * avoid these types by using 64-bit mappings when available. */ public class LibCUtil { From ef8071430b245068efa6c36b576738a6b37dc556 Mon Sep 17 00:00:00 2001 From: Daniel Widdis Date: Tue, 16 Jun 2020 13:06:51 -0700 Subject: [PATCH 7/9] Remove tests for removed mappings --- .../com/sun/jna/platform/linux/LibCTest.java | 32 +++---------------- 1 file changed, 4 insertions(+), 28 deletions(-) diff --git a/contrib/platform/test/com/sun/jna/platform/linux/LibCTest.java b/contrib/platform/test/com/sun/jna/platform/linux/LibCTest.java index b2e9b479c1..50a3c661b1 100644 --- a/contrib/platform/test/com/sun/jna/platform/linux/LibCTest.java +++ b/contrib/platform/test/com/sun/jna/platform/linux/LibCTest.java @@ -23,25 +23,19 @@ */ package com.sun.jna.platform.linux; -import com.sun.jna.Native; -import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.List; +import java.nio.file.FileStore; +import java.nio.file.Files; +import java.nio.file.Paths; import org.junit.Test; +import com.sun.jna.Native; import com.sun.jna.platform.linux.LibC.Statvfs; import com.sun.jna.platform.linux.LibC.Sysinfo; -import com.sun.jna.platform.unix.LibCAPI.off_t; import com.sun.jna.platform.unix.LibCAPI.size_t; import com.sun.jna.platform.unix.LibCAPI.ssize_t; -import java.nio.file.FileStore; -import java.nio.file.Files; -import java.nio.file.Paths; - import junit.framework.TestCase; /** @@ -56,10 +50,6 @@ public void testSizeTypes() { assertEquals("Wrong size_t value", VALUE, st.longValue()); ssize_t sst = new ssize_t(VALUE); assertEquals("Wrong ssize_t value", VALUE, sst.longValue()); - off_t ot = new off_t(VALUE); - assertEquals("Wrong off_t value", VALUE, ot.longValue()); - ot = new off_t(VALUE, true); - assertEquals("Wrong 64 bit off_t value", VALUE, ot.longValue()); } @Test @@ -98,18 +88,4 @@ public void testStatvfs() throws IOException, InterruptedException { assertTrue(vfs.f_ffree.longValue() <= vfs.f_files.longValue()); assertTrue(vfs.f_namemax.longValue() > 0); } - - private static List mounts() throws IOException, InterruptedException { - Process p = Runtime.getRuntime().exec("mount"); - - ArrayList mounts = new ArrayList(); - BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); - String line; - while ((line = reader.readLine()) != null) { - mounts.add(line); - } - p.waitFor(); - reader.close(); - return mounts; - } } From c1fef5f293db3e6576cd99583fc8a723c9b79655 Mon Sep 17 00:00:00 2001 From: Daniel Widdis Date: Wed, 17 Jun 2020 23:02:25 -0700 Subject: [PATCH 8/9] Get random filename without creating the file --- .../com/sun/jna/platform/linux/LibRTTest.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/contrib/platform/test/com/sun/jna/platform/linux/LibRTTest.java b/contrib/platform/test/com/sun/jna/platform/linux/LibRTTest.java index 73a0ce3b7a..834986d4df 100644 --- a/contrib/platform/test/com/sun/jna/platform/linux/LibRTTest.java +++ b/contrib/platform/test/com/sun/jna/platform/linux/LibRTTest.java @@ -37,6 +37,7 @@ import java.io.File; import java.io.IOException; +import java.security.SecureRandom; import org.junit.Test; @@ -54,10 +55,16 @@ public class LibRTTest extends TestCase { @Test public void testMmapToShm() throws IOException { - // Get a suitably random filename of the form "/somename" - File f = File.createTempFile("mmapToShm", "test"); - String share = "/" + f.getName(); - f.delete(); + // Get a suitably random filename of the form "/somename" to use as both the + // share virtual filename and the string to store + // Use same algorithm as File.CreateTempFile without creating a file + long n = new SecureRandom().nextLong(); + if (n == Long.MIN_VALUE) { + n = 0; // corner case + } else { + n = Math.abs(n); + } + String share = "/mmapToShm" + Long.toString(n) + "test"; // Get a file descriptor to the share. int fd = LIBRT.shm_open(share, O_RDWR | O_CREAT | O_EXCL, S_IRWXU); assertNotEquals("Failed to shm_open " + share + ". Error: " + Native.getLastError(), -1, fd); From ef1dd72253f8cd43e42acf73e6f4606744f8642c Mon Sep 17 00:00:00 2001 From: Daniel Widdis Date: Wed, 17 Jun 2020 23:03:20 -0700 Subject: [PATCH 9/9] Get random virtual filename without creating file --- contrib/platform/test/com/sun/jna/platform/linux/LibRTTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/contrib/platform/test/com/sun/jna/platform/linux/LibRTTest.java b/contrib/platform/test/com/sun/jna/platform/linux/LibRTTest.java index 834986d4df..d7af5b306e 100644 --- a/contrib/platform/test/com/sun/jna/platform/linux/LibRTTest.java +++ b/contrib/platform/test/com/sun/jna/platform/linux/LibRTTest.java @@ -35,7 +35,6 @@ import static com.sun.jna.platform.linux.Mman.PROT_WRITE; import static org.junit.Assert.assertNotEquals; -import java.io.File; import java.io.IOException; import java.security.SecureRandom;