3
\$\begingroup\$

Below is the approach taken in the function allocpath() in the book "Advanced Programming in Unix Environment", which I have modified to simply determine the maximum size of the path.

FILENAME_MAX from <stdio.h> has not been used because the documentation for GNU C Library states:

Unlike PATH_MAX, this macro is defined even if there is no actual limit imposed. In such a case, its value is typically a very large number. This is always the case on GNU/Hurd systems.

Usage Note: Don’t use FILENAME_MAX as the size of an array in which to store a file name! You can’t possibly make an array that big!

glibc also seems to be using the same approach here: realpath().

For UNIX-like systems, Vim does not meddle with sysconf()/pathconf() and simply does:

/* Use the system path length if it makes sense. */
#if defined(PATH_MAX) && PATH_MAX > 1000
 #define PATH_MAXL PATH_MAX
#else
 #define PATH_MAXL 1024
#endif

and similar for Windows.

CPython has a similar check as Vim.

Code:

#define _POSIX_C_SOURCE 200819
#define _XOPEN_SOURCE 700
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <unistd.h>
#ifdef PATH_MAX
 static long path_max = PATH_MAX;
#else
 static long path_max = 0;
#endif /* PATH_MAX */
static long posix_version = 0;
static long xsi_version = 0;
/* If PATH_MAX is indeterminate, no guarantee this is adequate. */
#define PATH_MAX_GUESS 1024
ssize_t get_path_max(void) 
{
 if (path_max == 0) { /* First time through. */
 errno = 0;
 if ((path_max = pathconf("/", _PC_PATH_MAX)) == -1) {
 if (errno != 0) {
 return -1;
 }
 path_max = PATH_MAX_GUESS; /* It's indeterminate */
 } else {
 path_max++; /* Add one since it is relative to root. */
 }
 }
 if (posix_version == 0) {
 if ((posix_version = sysconf(_SC_VERSION)) == -1) {
 return path_max;
 }
 }
 if (xsi_version == 0) {
 if ((xsi_version = sysconf(_SC_XOPEN_VERSION)) == -1) {
 return path_max;
 }
 }
 /* Before POSIX.1-2001, we aren't guaranteed that PATH_MAX includes
 * the terminating null byte. Same goes for XPG3. */
 ssize_t size = path_max;
 if ((posix_version < 200112L) && (xsi_version < 4)) {
 return path_max + 1;
 }
 return path_max;
}
int main(void)
{
 printf("%zu\n", get_path_max());
}

Review Request:

Is this reliable? Can it be made more robust?

asked Jun 19, 2024 at 10:53
\$\endgroup\$
0

1 Answer 1

3
\$\begingroup\$

Your code already seems good (it follows C good practices and it's well written, overall). These are the enhancements I'd implement:

1) Add bounds checking for the path_max value

#define MIN_PATH_LENGTH 256
#define MAX_PATH_LENGTH 32767 

I set MAX_PATH_LENGTH to a value that works on many modern systems, if you have particular requirements you may change it.

2) Generic function overhaul

ssize_t get_path_max(void) 
{
 if (path_max == 0) {
 errno = 0;
 path_max = pathconf("/", _PC_PATH_MAX);
 
 if (path_max == -1) {
 if (errno != 0) {
 return -1;
 }
 path_max = PATH_MAX_GUESS;
 } else {
 path_max++; /* Add one for root */
 }
 
 /* Sanity check the value */
 if (path_max < MIN_PATH_LENGTH) {
 path_max = MIN_PATH_LENGTH;
 } else if (path_max > MAX_PATH_LENGTH) {
 path_max = MAX_PATH_LENGTH;
 }
 }
 /* Cache POSIX/XSI versions */
 if (posix_version == 0) {
 posix_version = sysconf(_SC_VERSION);
 if (posix_version == -1) {
 posix_version = 200809L; /* Assume recent POSIX */
 }
 }
 if (xsi_version == 0) {
 xsi_version = sysconf(_SC_XOPEN_VERSION);
 if (xsi_version == -1) {
 xsi_version = 700; /* Assume recent XSI */
 }
 }
 /* Add space for null terminator for older standards */
 if ((posix_version < 200112L) && (xsi_version < 4)) {
 return path_max + 1;
 }
 return path_max;
}

This approach is more reliable than using FILENAME_MAX and provides better portability across different UNIX-like systems. The bounds checking helps prevent possibly wrong values, while still maintaining flexibility for different systems.

toolic
14.7k5 gold badges29 silver badges204 bronze badges
answered Nov 5, 2024 at 19:43
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.