diff --git a/CHANGES.md b/CHANGES.md index 3984fcf383..09ebd6b588 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -17,6 +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` 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). * [#1209](https://github.com/java-native-access/jna/pull/1209): Add mappings for `Thread32First` and `Thread32Next` to `c.s.j.p.win32.Kernel32` - [@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..95b59fc233 --- /dev/null +++ b/contrib/platform/src/com/sun/jna/platform/linux/ErrNo.java @@ -0,0 +1,162 @@ +/* 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; + +/** + * 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/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 27da5b7e6b..c2a9b9485e 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,10 @@ */ package com.sun.jna.platform.unix; +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 * Linux-like O/S-es to implement the same API, but maybe using a different @@ -30,6 +34,42 @@ * @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); + } + } + // see man(2) get/set uid/gid int getuid(); int geteuid(); @@ -98,4 +138,68 @@ public interface LibCAPI extends Reboot, Resource { * @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); + + /** + * Flushes changes made to the in-core copy of a file that was mapped into + * 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 + * 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..36499834df --- /dev/null +++ b/contrib/platform/src/com/sun/jna/platform/unix/LibCUtil.java @@ -0,0 +1,174 @@ +/* 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. 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 { + + private static final NativeLibrary LIBC = NativeLibrary.getInstance("c"); + + 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() { + } + + /** + * 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 (mmap64 || Native.LONG_SIZE > 4) { + params[5] = offset; + } else { + require32Bit(offset, "offset"); + params[5] = (int) 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/LibCTest.java b/contrib/platform/test/com/sun/jna/platform/linux/LibCTest.java index 5f45d50de3..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,20 +23,18 @@ */ 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 java.nio.file.FileStore; -import java.nio.file.Files; -import java.nio.file.Paths; +import com.sun.jna.platform.unix.LibCAPI.size_t; +import com.sun.jna.platform.unix.LibCAPI.ssize_t; import junit.framework.TestCase; @@ -45,6 +43,15 @@ */ 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()); + } + @Test public void testSysinfo() { Sysinfo info = new Sysinfo(); @@ -81,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; - } } 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..d7af5b306e --- /dev/null +++ b/contrib/platform/test/com/sun/jna/platform/linux/LibRTTest.java @@ -0,0 +1,126 @@ +/* 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.IOException; +import java.security.SecureRandom; + +import org.junit.Test; + +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.platform.unix.LibCAPI.size_t; +import com.sun.jna.platform.unix.LibCUtil; + +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" 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); + try { + // Multiply by 4 to handle all possible encodings + int bufLen = 4 * (share.length() + 1); + size_t length = new size_t(bufLen); + // Allocate memory to the share (fills with null bytes) + 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 = 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); + 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. 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); + 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); + } + } +}