aboutsummaryrefslogtreecommitdiff
path: root/lib/libc
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libc')
-rw-r--r--lib/libc/gen/_rand48.c34
-rw-r--r--lib/libc/gen/drand48.c6
-rw-r--r--lib/libc/gen/erand48.c9
-rw-r--r--lib/libc/gen/fts.3146
-rw-r--r--lib/libc/gen/fts.c3
-rw-r--r--lib/libc/gen/getgrouplist.333
-rw-r--r--lib/libc/gen/initgroups.38
-rw-r--r--lib/libc/gen/jrand48.c7
-rw-r--r--lib/libc/gen/lcong48.c12
-rw-r--r--lib/libc/gen/lrand48.c6
-rw-r--r--lib/libc/gen/mrand48.c8
-rw-r--r--lib/libc/gen/nrand48.c6
-rw-r--r--lib/libc/gen/rand48.35
-rw-r--r--lib/libc/gen/rand48.h61
-rw-r--r--lib/libc/gen/seed48.c18
-rw-r--r--lib/libc/gen/srand48.c13
-rw-r--r--lib/libc/stdlib/realpath.312
-rw-r--r--lib/libc/stdlib/realpath.c14
-rw-r--r--lib/libc/stdtime/strptime.32
-rw-r--r--lib/libc/stdtime/strptime.c3
-rw-r--r--lib/libc/tests/gen/realpath2_test.c106
21 files changed, 363 insertions, 149 deletions
diff --git a/lib/libc/gen/_rand48.c b/lib/libc/gen/_rand48.c
index 990e2c86949b..114c1595b33d 100644
--- a/lib/libc/gen/_rand48.c
+++ b/lib/libc/gen/_rand48.c
@@ -13,34 +13,6 @@
#include "rand48.h"
-unsigned short _rand48_seed[3] = {
- RAND48_SEED_0,
- RAND48_SEED_1,
- RAND48_SEED_2
-};
-unsigned short _rand48_mult[3] = {
- RAND48_MULT_0,
- RAND48_MULT_1,
- RAND48_MULT_2
-};
-unsigned short _rand48_add = RAND48_ADD;
-
-void
-_dorand48(unsigned short xseed[3])
-{
- unsigned long accu;
- unsigned short temp[2];
-
- accu = (unsigned long) _rand48_mult[0] * (unsigned long) xseed[0] +
- (unsigned long) _rand48_add;
- temp[0] = (unsigned short) accu; /* lower 16 bits */
- accu >>= sizeof(unsigned short) * 8;
- accu += (unsigned long) _rand48_mult[0] * (unsigned long) xseed[1] +
- (unsigned long) _rand48_mult[1] * (unsigned long) xseed[0];
- temp[1] = (unsigned short) accu; /* middle 16 bits */
- accu >>= sizeof(unsigned short) * 8;
- accu += _rand48_mult[0] * xseed[2] + _rand48_mult[1] * xseed[1] + _rand48_mult[2] * xseed[0];
- xseed[0] = temp[0];
- xseed[1] = temp[1];
- xseed[2] = (unsigned short) accu;
-}
+uint48 _rand48_seed = RAND48_SEED;
+uint48 _rand48_mult = RAND48_MULT;
+uint48 _rand48_add = RAND48_ADD;
diff --git a/lib/libc/gen/drand48.c b/lib/libc/gen/drand48.c
index cec04a6a2425..f7f43ff20468 100644
--- a/lib/libc/gen/drand48.c
+++ b/lib/libc/gen/drand48.c
@@ -13,10 +13,10 @@
#include "rand48.h"
-extern unsigned short _rand48_seed[3];
-
double
drand48(void)
{
- return erand48(_rand48_seed);
+ ERAND48_BEGIN;
+ _DORAND48(_rand48_seed);
+ ERAND48_END(_rand48_seed);
}
diff --git a/lib/libc/gen/erand48.c b/lib/libc/gen/erand48.c
index 286904c27839..38d4774a9fe6 100644
--- a/lib/libc/gen/erand48.c
+++ b/lib/libc/gen/erand48.c
@@ -16,8 +16,9 @@
double
erand48(unsigned short xseed[3])
{
- _dorand48(xseed);
- return ldexp((double) xseed[0], -48) +
- ldexp((double) xseed[1], -32) +
- ldexp((double) xseed[2], -16);
+ uint48 tmp;
+
+ ERAND48_BEGIN;
+ DORAND48(tmp, xseed);
+ ERAND48_END(tmp);
}
diff --git a/lib/libc/gen/fts.3 b/lib/libc/gen/fts.3
index b937607b48e0..da304e59ee72 100644
--- a/lib/libc/gen/fts.3
+++ b/lib/libc/gen/fts.3
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd October 1, 2025
+.Dd October 6, 2025
.Dt FTS 3
.Os
.Sh NAME
@@ -69,14 +69,15 @@ on a file hierarchy, which is then supplied to
the other
.Nm
functions.
-The function
+The
.Fn fts_read
-returns a pointer to a structure describing one of the files in the file
-hierarchy.
-The function
+function returns a pointer to a structure describing one of the files
+in the file hierarchy.
+The
.Fn fts_children
-returns a pointer to a linked list of structures, each of which describes
-one of the files contained in a directory in the hierarchy.
+function returns a pointer to a linked list of structures, each of
+which describes one of the files contained in a directory in the
+hierarchy.
In general, directories are visited two distinguishable times; in pre-order
(before any of their descendants are visited) and in post-order (after all
of their descendants have been visited).
@@ -544,10 +545,10 @@ from descending into directories that have a different device number
than the file from which the descent began.
.El
.Pp
-The argument
-.Fn compar
-specifies a user-defined function which may be used to order the traversal
-of the hierarchy.
+The
+.Fa compar
+argument points to a user-defined function which may be used to order
+the traversal of the hierarchy.
It
takes two pointers to pointers to
.Vt FTSENT
@@ -625,6 +626,15 @@ structure is returned, and
.Va errno
may or may not have been set (see
.Fa fts_info ) .
+Note that
+.Fn fts_read
+will not set
+.Va errno
+to 0 if called again with the same
+.Fa ftsp
+argument after the
+.Dv FTS_STOP
+flag has been set or the end of the stream has been reached.
.Pp
The
.Vt FTSENT
@@ -639,9 +649,9 @@ directory, in which case they will not be overwritten until after a call to
.Fn fts_read
after the
.Vt FTSENT
-structure has been returned by the function
+structure has been returned by the
.Fn fts_read
-in post-order.
+function in post-order.
.Ss Fn fts_children
The
.Fn fts_children
@@ -717,10 +727,10 @@ and
fields.
.El
.Ss Fn fts_set
-The function
+The
.Fn fts_set
-allows the user application to determine further processing for the
-file
+function allows the user application to determine further processing
+for the file
.Fa f
of the stream
.Fa ftsp .
@@ -786,6 +796,39 @@ The file may be one of those most recently returned by either
or
.Fn fts_read .
.El
+.Ss Fn fts_set_clientptr , Fn fts_get_clientptr
+The
+.Fn fts_set_clientptr
+function sets the client data pointer for the stream
+.Fa ftsp
+to
+.Fa clientdata .
+The
+.Fn fts_get_clientptr
+function returns the client data pointer associated with
+.Fa ftsp .
+This can be used to pass per-stream data to the comparison function.
+.Pp
+For performance reasons,
+.Fn fts_get_clientptr
+may be shadowed by a preprocessor macro.
+.Ss Fn fts_get_stream
+The
+.Fn fts_get_stream
+function returns the
+.Nm
+stream associated with the file entry
+.Fa f .
+A typical use for this would be for a comparison function to first call
+.Fn fts_get_stream
+on one of its arguments, then call
+.Fn fts_get_clientptr
+to obtain the client data pointer, which in turn points to information
+necessary to correctly order the two entries.
+.Pp
+For performance reasons,
+.Fn fts_get_stream
+may be shadowed by a preprocessor macro.
.Ss Fn fts_close
The
.Fn fts_close
@@ -797,6 +840,75 @@ or
.Fn fts_open_b
was called to open
.Fa ftsp .
+.Sh RETURN VALUES
+The
+.Fn fts_open
+and
+.Fn fts_open_b
+functions return a pointer to the new
+.Nm
+stream on success and
+.Dv NULL
+on failure.
+.Pp
+The
+.Fn fts_read
+function returns a pointer to the next file entry on success, or if an
+error occurs that relates specifically to that file entry.
+On reaching the end of the file hierarchy, it returns
+.Dv NULL
+and sets the external variable
+.Va errno
+to 0.
+On failure, it returns
+.Dv NULL
+and sets
+.Va errno
+to an appropriate non-zero value.
+If called again after the
+.Dv FTS_STOP
+flag has been set or the end of the stream has been reached,
+.Fn fts_read
+returns
+.Dv NULL
+and leaves
+.Va errno
+untouched.
+.Pp
+The
+.Fn fts_children
+function returns a pointer to a linked list of file entries on
+success.
+On reaching the end of the file hierarchy, it returns
+.Dv NULL
+and sets the external variable
+.Va errno
+to 0.
+On failure, it returns
+.Dv NULL
+and sets
+.Va errno
+to an appropriate non-zero value.
+.Pp
+The
+.Fn fts_set
+function returns 0 on success and \-1 if its
+.Fa instr
+argument is invalid.
+.Pp
+The
+.Fn fts_get_clientptr
+function returns the client data pointer associated with its argument,
+or
+.Dv NULL
+if none has been set.
+.Pp
+The
+.Fn fts_get_stream
+function returns a pointer to the
+.Nm
+stream associated with its argument.
+.Pp
The
.Fn fts_close
function
@@ -853,7 +965,7 @@ functions may fail and set
as follows:
.Bl -tag -width Er
.It Bq Er EINVAL
-The options were invalid, or the list were empty.
+The options were invalid, or the list was empty.
.El
.Sh SEE ALSO
.Xr find 1 ,
diff --git a/lib/libc/gen/fts.c b/lib/libc/gen/fts.c
index cce959ba836a..4aa386d777cd 100644
--- a/lib/libc/gen/fts.c
+++ b/lib/libc/gen/fts.c
@@ -106,7 +106,6 @@ struct _fts_private {
* This assumption only holds for UFS-like filesystems that implement
* links and directories this way, so we must punt for others.
*/
-
static const char *ufslike_filesystems[] = {
"ufs",
"zfs",
@@ -679,7 +678,6 @@ fts_children(FTS *sp, int instr)
void *
(fts_get_clientptr)(FTS *sp)
{
-
return (fts_get_clientptr(sp));
}
@@ -696,7 +694,6 @@ FTS *
void
fts_set_clientptr(FTS *sp, void *clientptr)
{
-
sp->fts_clientptr = clientptr;
}
diff --git a/lib/libc/gen/getgrouplist.3 b/lib/libc/gen/getgrouplist.3
index e3939fc2481a..9e05ff7e7a29 100644
--- a/lib/libc/gen/getgrouplist.3
+++ b/lib/libc/gen/getgrouplist.3
@@ -33,7 +33,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd August 29, 2025
+.Dd October 9, 2025
.Dt GETGROUPLIST 3
.Os
.Sh NAME
@@ -48,30 +48,37 @@
.Sh DESCRIPTION
The
.Fn getgrouplist
-function reads through the group database to retrieve the supplementary groups
-for the user specified in
-.Fa name ,
+function retrieves from the group database the supplementary groups for the user
+specified in
+.Fa name
and returns the effective group list, whose first group is the value of
.Fa basegid
-and the others are the retrieved supplementary groups.
+and the others are the supplementary groups.
.Fa basegid
-typically is the user's group number from the password database.
+typically is the user's initial numerical group ID from the password database.
.Pp
The effective group list is returned in the array pointed to by
.Fa groups .
-The caller specifies the size of the
+The caller specifies the length of the
.Fa groups
array in the integer pointed to by
-.Fa ngroups ;
-the actual number of groups found is returned in
+.Fa ngroups .
+The number of groups of the effective group list, which may be greater than the
+.Fa groups
+array's length, is returned through
.Fa ngroups .
.Sh RETURN VALUES
The
.Fn getgrouplist
-function
-returns 0 on success and \-1 if the size of the group list is too small to
-hold all the user's groups.
-Here, the group array will be filled with as many groups as will fit.
+function returns 0 on success and \-1 if the length of the group list is too
+small to hold all the user's groups.
+In the latter case, the
+.Fa groups
+array is filled with as many groups as possible from the start of the effective
+group list, and the length pointed to by
+.Fa ngroups
+is set to the full length of the latter, thus to a value strictly greater than
+before the call.
.Sh FILES
.Bl -tag -width /etc/group -compact
.It Pa /etc/group
diff --git a/lib/libc/gen/initgroups.3 b/lib/libc/gen/initgroups.3
index 4f538fb180ec..74133e7d7048 100644
--- a/lib/libc/gen/initgroups.3
+++ b/lib/libc/gen/initgroups.3
@@ -33,7 +33,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd September 17, 2025
+.Dd October 9, 2025
.Dt INITGROUPS 3
.Os
.Sh NAME
@@ -67,9 +67,9 @@ The
.Fn initgroups
function may fail and set
.Va errno
-to any of the errors specified for the library function
-.Xr setgroups 2 .
-It may also return:
+to any of the errors specified for the
+.Xr setgroups 2
+system call, or to:
.Bl -tag -width Er
.It Bq Er ENOMEM
The
diff --git a/lib/libc/gen/jrand48.c b/lib/libc/gen/jrand48.c
index 0a9f780a9e5c..93442439d49e 100644
--- a/lib/libc/gen/jrand48.c
+++ b/lib/libc/gen/jrand48.c
@@ -11,14 +11,13 @@
* to anyone/anything when using this software.
*/
-#include <stdint.h>
-
#include "rand48.h"
long
jrand48(unsigned short xseed[3])
{
+ uint48 tmp;
- _dorand48(xseed);
- return ((int32_t)(((uint32_t)xseed[2] << 16) | (uint32_t)xseed[1]));
+ DORAND48(tmp, xseed);
+ return ((int)((tmp >> 16) & 0xffffffff));
}
diff --git a/lib/libc/gen/lcong48.c b/lib/libc/gen/lcong48.c
index f13826b3d3f3..871b2110ed94 100644
--- a/lib/libc/gen/lcong48.c
+++ b/lib/libc/gen/lcong48.c
@@ -13,18 +13,10 @@
#include "rand48.h"
-extern unsigned short _rand48_seed[3];
-extern unsigned short _rand48_mult[3];
-extern unsigned short _rand48_add;
-
void
lcong48(unsigned short p[7])
{
- _rand48_seed[0] = p[0];
- _rand48_seed[1] = p[1];
- _rand48_seed[2] = p[2];
- _rand48_mult[0] = p[3];
- _rand48_mult[1] = p[4];
- _rand48_mult[2] = p[5];
+ LOADRAND48(_rand48_seed, &p[0]);
+ LOADRAND48(_rand48_mult, &p[3]);
_rand48_add = p[6];
}
diff --git a/lib/libc/gen/lrand48.c b/lib/libc/gen/lrand48.c
index a3d0111cf4d5..cc07044b8af9 100644
--- a/lib/libc/gen/lrand48.c
+++ b/lib/libc/gen/lrand48.c
@@ -13,11 +13,9 @@
#include "rand48.h"
-extern unsigned short _rand48_seed[3];
-
long
lrand48(void)
{
- _dorand48(_rand48_seed);
- return ((long) _rand48_seed[2] << 15) + ((long) _rand48_seed[1] >> 1);
+ _DORAND48(_rand48_seed);
+ return (_rand48_seed >> 17) & 0x7fffffff;
}
diff --git a/lib/libc/gen/mrand48.c b/lib/libc/gen/mrand48.c
index 15b0bfb1bd6e..f9128a6d4188 100644
--- a/lib/libc/gen/mrand48.c
+++ b/lib/libc/gen/mrand48.c
@@ -15,13 +15,9 @@
#include "rand48.h"
-extern unsigned short _rand48_seed[3];
-
long
mrand48(void)
{
-
- _dorand48(_rand48_seed);
- return ((int32_t)(((uint32_t)_rand48_seed[2] << 16) |
- (uint32_t)_rand48_seed[1]));
+ _DORAND48(_rand48_seed);
+ return ((int)((_rand48_seed >> 16) & 0xffffffff));
}
diff --git a/lib/libc/gen/nrand48.c b/lib/libc/gen/nrand48.c
index 6c54065e7e0f..f6f4e231105c 100644
--- a/lib/libc/gen/nrand48.c
+++ b/lib/libc/gen/nrand48.c
@@ -16,6 +16,8 @@
long
nrand48(unsigned short xseed[3])
{
- _dorand48(xseed);
- return ((long) xseed[2] << 15) + ((long) xseed[1] >> 1);
+ uint48 tmp;
+
+ DORAND48(tmp, xseed);
+ return ((tmp >> 17) & 0x7fffffff);
}
diff --git a/lib/libc/gen/rand48.3 b/lib/libc/gen/rand48.3
index 1e47c843058e..3ea649354270 100644
--- a/lib/libc/gen/rand48.3
+++ b/lib/libc/gen/rand48.3
@@ -9,7 +9,7 @@
.\" of any kind. I shall in no event be liable for anything that happens
.\" to anyone/anything when using this software.
.\"
-.Dd September 4, 2012
+.Dd September 11, 2025
.Dt RAND48 3
.Os
.Sh NAME
@@ -183,5 +183,8 @@ generator calls.
.Xr arc4random 3 ,
.Xr rand 3 ,
.Xr random 3
+.Sh STANDARDS
+The functions described in this page are expected to conform to
+.St -p1003.1-2008 .
.Sh AUTHORS
.An Martin Birgmeier
diff --git a/lib/libc/gen/rand48.h b/lib/libc/gen/rand48.h
index 9861e99683cb..d3326e851491 100644
--- a/lib/libc/gen/rand48.h
+++ b/lib/libc/gen/rand48.h
@@ -14,10 +14,11 @@
#ifndef _RAND48_H_
#define _RAND48_H_
+#include <sys/types.h>
#include <math.h>
#include <stdlib.h>
-void _dorand48(unsigned short[3]);
+#include "fpmath.h"
#define RAND48_SEED_0 (0x330e)
#define RAND48_SEED_1 (0xabcd)
@@ -27,4 +28,62 @@ void _dorand48(unsigned short[3]);
#define RAND48_MULT_2 (0x0005)
#define RAND48_ADD (0x000b)
+typedef uint64_t uint48;
+
+extern uint48 _rand48_seed;
+extern uint48 _rand48_mult;
+extern uint48 _rand48_add;
+
+#define TOUINT48(x, y, z) \
+ ((uint48)(x) + (((uint48)(y)) << 16) + (((uint48)(z)) << 32))
+
+#define RAND48_SEED TOUINT48(RAND48_SEED_0, RAND48_SEED_1, RAND48_SEED_2)
+#define RAND48_MULT TOUINT48(RAND48_MULT_0, RAND48_MULT_1, RAND48_MULT_2)
+
+#define LOADRAND48(l, x) do { \
+ (l) = TOUINT48((x)[0], (x)[1], (x)[2]); \
+} while (0)
+
+#define STORERAND48(l, x) do { \
+ (x)[0] = (unsigned short)(l); \
+ (x)[1] = (unsigned short)((l) >> 16); \
+ (x)[2] = (unsigned short)((l) >> 32); \
+} while (0)
+
+#define _DORAND48(l) do { \
+ (l) = (l) * _rand48_mult + _rand48_add; \
+} while (0)
+
+#define DORAND48(l, x) do { \
+ LOADRAND48(l, x); \
+ _DORAND48(l); \
+ STORERAND48(l, x); \
+} while (0)
+
+#define ERAND48_BEGIN \
+ union { \
+ union IEEEd2bits ieee; \
+ uint64_t u64; \
+ } u; \
+ int s
+
+/*
+ * Optimization for speed: assume doubles are IEEE 754 and use bit fiddling
+ * rather than converting to double. Specifically, clamp the result to 48 bits
+ * and convert to a double in [0.0, 1.0) via division by 2^48. Normalize by
+ * shifting the most significant bit into the implicit one position and
+ * adjusting the exponent accordingly. The store to the exponent field
+ * overwrites the implicit one.
+ */
+#define ERAND48_END(x) do { \
+ u.u64 = ((x) & 0xffffffffffffULL); \
+ if (u.u64 == 0) \
+ return (0.0); \
+ u.u64 <<= 5; \
+ for (s = 0; !(u.u64 & (1LL << 52)); s++, u.u64 <<= 1) \
+ ; \
+ u.ieee.bits.exp = 1022 - s; \
+ return (u.ieee.d); \
+} while (0)
+
#endif /* _RAND48_H_ */
diff --git a/lib/libc/gen/seed48.c b/lib/libc/gen/seed48.c
index 258c4bac3c9f..f57656ce1121 100644
--- a/lib/libc/gen/seed48.c
+++ b/lib/libc/gen/seed48.c
@@ -13,24 +13,14 @@
#include "rand48.h"
-extern unsigned short _rand48_seed[3];
-extern unsigned short _rand48_mult[3];
-extern unsigned short _rand48_add;
-
unsigned short *
seed48(unsigned short xseed[3])
{
static unsigned short sseed[3];
- sseed[0] = _rand48_seed[0];
- sseed[1] = _rand48_seed[1];
- sseed[2] = _rand48_seed[2];
- _rand48_seed[0] = xseed[0];
- _rand48_seed[1] = xseed[1];
- _rand48_seed[2] = xseed[2];
- _rand48_mult[0] = RAND48_MULT_0;
- _rand48_mult[1] = RAND48_MULT_1;
- _rand48_mult[2] = RAND48_MULT_2;
+ STORERAND48(_rand48_seed, sseed);
+ LOADRAND48(_rand48_seed, xseed);
+ _rand48_mult = RAND48_MULT;
_rand48_add = RAND48_ADD;
- return sseed;
+ return (sseed);
}
diff --git a/lib/libc/gen/srand48.c b/lib/libc/gen/srand48.c
index fd369a094c51..4b82ece72db8 100644
--- a/lib/libc/gen/srand48.c
+++ b/lib/libc/gen/srand48.c
@@ -13,18 +13,11 @@
#include "rand48.h"
-extern unsigned short _rand48_seed[3];
-extern unsigned short _rand48_mult[3];
-extern unsigned short _rand48_add;
-
void
srand48(long seed)
{
- _rand48_seed[0] = RAND48_SEED_0;
- _rand48_seed[1] = (unsigned short) seed;
- _rand48_seed[2] = (unsigned short) (seed >> 16);
- _rand48_mult[0] = RAND48_MULT_0;
- _rand48_mult[1] = RAND48_MULT_1;
- _rand48_mult[2] = RAND48_MULT_2;
+ _rand48_seed = TOUINT48(RAND48_SEED_0, (unsigned short)seed,
+ (unsigned short)(seed >> 16));
+ _rand48_mult = RAND48_MULT;
_rand48_add = RAND48_ADD;
}
diff --git a/lib/libc/stdlib/realpath.3 b/lib/libc/stdlib/realpath.3
index 065ba312c2ef..76f40249963b 100644
--- a/lib/libc/stdlib/realpath.3
+++ b/lib/libc/stdlib/realpath.3
@@ -28,7 +28,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd May 11, 2012
+.Dd October 10, 2025
.Dt REALPATH 3
.Os
.Sh NAME
@@ -108,11 +108,11 @@ and
.Xr getcwd 3 .
.Sh SEE ALSO
.Xr getcwd 3
-.\" .Sh STANDARDS
-.\" The
-.\" .Fn realpath
-.\" function conforms to
-.\" .St -p1003.1-2001 .
+.Sh STANDARDS
+The
+.Fn realpath
+function conforms to
+.St -p1003.1-2001 .
.Sh HISTORY
The
.Fn realpath
diff --git a/lib/libc/stdlib/realpath.c b/lib/libc/stdlib/realpath.c
index 4c52b73319ab..18f29e95ee6b 100644
--- a/lib/libc/stdlib/realpath.c
+++ b/lib/libc/stdlib/realpath.c
@@ -49,7 +49,7 @@ realpath1(const char *path, char *resolved)
{
struct stat sb;
char *p, *q;
- size_t left_len, resolved_len, next_token_len;
+ size_t left_len, prev_len, resolved_len, next_token_len;
unsigned symlinks;
ssize_t slen;
char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX];
@@ -98,6 +98,7 @@ realpath1(const char *path, char *resolved)
left_len = 0;
}
+ prev_len = resolved_len;
if (resolved[resolved_len - 1] != '/') {
if (resolved_len + 1 >= PATH_MAX) {
errno = ENAMETOOLONG;
@@ -133,8 +134,17 @@ realpath1(const char *path, char *resolved)
errno = ENAMETOOLONG;
return (NULL);
}
- if (lstat(resolved, &sb) != 0)
+ if (lstat(resolved, &sb) != 0) {
+ /*
+ * EACCES means the parent directory is not
+ * readable, while ENOTDIR means the parent
+ * directory is not a directory. Rewind the path
+ * to correctly indicate where the error lies.
+ */
+ if (errno == EACCES || errno == ENOTDIR)
+ resolved[prev_len] = '\0';
return (NULL);
+ }
if (S_ISLNK(sb.st_mode)) {
if (symlinks++ > MAXSYMLINKS) {
errno = ELOOP;
diff --git a/lib/libc/stdtime/strptime.3 b/lib/libc/stdtime/strptime.3
index 7df73d2d080a..9456fa757b85 100644
--- a/lib/libc/stdtime/strptime.3
+++ b/lib/libc/stdtime/strptime.3
@@ -171,7 +171,7 @@ is taken as noon.
The
.Fa %Z
format specifier only accepts time zone abbreviations of the local time zone,
-or the value "GMT".
+and the values "GMT", "UTC", or "Z".
This limitation is because of ambiguity due to of the over loading of time
zone abbreviations.
One such example is
diff --git a/lib/libc/stdtime/strptime.c b/lib/libc/stdtime/strptime.c
index 5f1293c7a267..375e49146639 100644
--- a/lib/libc/stdtime/strptime.c
+++ b/lib/libc/stdtime/strptime.c
@@ -546,7 +546,8 @@ label:
zonestr[cp - buf] = '\0';
tzset();
if (0 == strcmp(zonestr, "GMT") ||
- 0 == strcmp(zonestr, "UTC")) {
+ 0 == strcmp(zonestr, "UTC") ||
+ 0 == strcmp(zonestr, "Z")) {
*GMTp = 1;
} else if (0 == strcmp(zonestr, tzname[0])) {
tm->tm_isdst = 0;
diff --git a/lib/libc/tests/gen/realpath2_test.c b/lib/libc/tests/gen/realpath2_test.c
index f89dd99cbb72..431df8721ae0 100644
--- a/lib/libc/tests/gen/realpath2_test.c
+++ b/lib/libc/tests/gen/realpath2_test.c
@@ -1,6 +1,7 @@
/*
* Copyright (c) 2017 Jan Kokemüller
* All rights reserved.
+ * Copyright (c) 2025 Klara, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -25,6 +26,8 @@
*/
#include <sys/param.h>
+#include <sys/stat.h>
+
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
@@ -34,6 +37,31 @@
#include <atf-c.h>
+ATF_TC(realpath_null);
+ATF_TC_HEAD(realpath_null, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test null input");
+}
+ATF_TC_BODY(realpath_null, tc)
+{
+ ATF_REQUIRE_ERRNO(EINVAL, realpath(NULL, NULL) == NULL);
+}
+
+ATF_TC(realpath_empty);
+ATF_TC_HEAD(realpath_empty, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test empty input");
+}
+ATF_TC_BODY(realpath_empty, tc)
+{
+ char resb[PATH_MAX] = "";
+
+ ATF_REQUIRE_EQ(0, mkdir("foo", 0755));
+ ATF_REQUIRE_EQ(0, chdir("foo"));
+ ATF_REQUIRE_ERRNO(ENOENT, realpath("", resb) == NULL);
+ ATF_REQUIRE_STREQ("", resb);
+}
+
ATF_TC(realpath_buffer_overflow);
ATF_TC_HEAD(realpath_buffer_overflow, tc)
{
@@ -44,16 +72,11 @@ ATF_TC_HEAD(realpath_buffer_overflow, tc)
ATF_TC_BODY(realpath_buffer_overflow, tc)
{
- char path[MAXPATHLEN] = { 0 };
- char resb[MAXPATHLEN] = { 0 };
- size_t i;
+ char path[PATH_MAX] = "";
+ char resb[PATH_MAX] = "";
- path[0] = 'a';
+ memset(path, 'a', sizeof(path) - 1);
path[1] = '/';
- for (i = 2; i < sizeof(path) - 1; ++i) {
- path[i] = 'a';
- }
-
ATF_REQUIRE(realpath(path, resb) == NULL);
}
@@ -66,9 +89,9 @@ ATF_TC_HEAD(realpath_empty_symlink, tc)
ATF_TC_BODY(realpath_empty_symlink, tc)
{
- char path[MAXPATHLEN] = { 0 };
- char slnk[MAXPATHLEN] = { 0 };
- char resb[MAXPATHLEN] = { 0 };
+ char path[PATH_MAX] = "";
+ char slnk[PATH_MAX] = "";
+ char resb[PATH_MAX] = "";
int fd;
(void)strlcat(slnk, "empty_symlink", sizeof(slnk));
@@ -89,11 +112,70 @@ ATF_TC_BODY(realpath_empty_symlink, tc)
ATF_REQUIRE(unlink(slnk) == 0);
}
-ATF_TP_ADD_TCS(tp)
+ATF_TC(realpath_partial);
+ATF_TC_HEAD(realpath_partial, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test that failure leaves a partial result");
+ atf_tc_set_md_var(tc, "require.user", "unprivileged");
+}
+
+ATF_TC_BODY(realpath_partial, tc)
{
+ char resb[PATH_MAX] = "";
+ size_t len;
+
+ /* scenario 1: missing directory */
+ ATF_REQUIRE_EQ(0, mkdir("foo", 0755));
+ ATF_REQUIRE_ERRNO(ENOENT, realpath("foo/bar/baz", resb) == NULL);
+ len = strnlen(resb, sizeof(resb));
+ ATF_REQUIRE(len > 8 && len < sizeof(resb));
+ ATF_REQUIRE_STREQ("/foo/bar", resb + len - 8);
+
+ /* scenario 2: dead link 1 */
+ ATF_REQUIRE_EQ(0, symlink("nix", "foo/bar"));
+ ATF_REQUIRE_ERRNO(ENOENT, realpath("foo/bar/baz", resb) == NULL);
+ len = strnlen(resb, sizeof(resb));
+ ATF_REQUIRE(len > 8 && len < sizeof(resb));
+ ATF_REQUIRE_STREQ("/foo/nix", resb + len - 8);
+
+ /* scenario 3: missing file */
+ ATF_REQUIRE_EQ(0, unlink("foo/bar"));
+ ATF_REQUIRE_EQ(0, mkdir("foo/bar", 0755));
+ ATF_REQUIRE_ERRNO(ENOENT, realpath("foo/bar/baz", resb) == NULL);
+ len = strnlen(resb, sizeof(resb));
+ ATF_REQUIRE(len > 12 && len < sizeof(resb));
+ ATF_REQUIRE_STREQ("/foo/bar/baz", resb + len - 12);
+
+ /* scenario 4: dead link 2 */
+ ATF_REQUIRE_EQ(0, symlink("nix", "foo/bar/baz"));
+ ATF_REQUIRE_ERRNO(ENOENT, realpath("foo/bar/baz", resb) == NULL);
+ len = strnlen(resb, sizeof(resb));
+ ATF_REQUIRE(len > 12 && len < sizeof(resb));
+ ATF_REQUIRE_STREQ("/foo/bar/nix", resb + len - 12);
+
+ /* scenario 5: unreadable directory */
+ ATF_REQUIRE_EQ(0, chmod("foo", 000));
+ ATF_REQUIRE_ERRNO(EACCES, realpath("foo/bar/baz", resb) == NULL);
+ len = strnlen(resb, sizeof(resb));
+ ATF_REQUIRE(len > 4 && len < sizeof(resb));
+ ATF_REQUIRE_STREQ("/foo", resb + len - 4);
+
+ /* scenario 6: not a directory */
+ ATF_REQUIRE_EQ(0, close(creat("bar", 0644)));
+ ATF_REQUIRE_ERRNO(ENOTDIR, realpath("bar/baz", resb) == NULL);
+ len = strnlen(resb, sizeof(resb));
+ ATF_REQUIRE(len > 4 && len < sizeof(resb));
+ ATF_REQUIRE_STREQ("/bar", resb + len - 4);
+}
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, realpath_null);
+ ATF_TP_ADD_TC(tp, realpath_empty);
ATF_TP_ADD_TC(tp, realpath_buffer_overflow);
ATF_TP_ADD_TC(tp, realpath_empty_symlink);
+ ATF_TP_ADD_TC(tp, realpath_partial);
return atf_no_error();
}