user14629932
user14629932

Reputation:

Is there any difference between FILENAME_MAX and PATH_MAX in C?

What gets me really confused is some programmers refer to the filename as what I refer to the path, eg. /Users/example/Desktop/file.txt. What I call the file name would just be file.txt, but apparently some people refer to the path /Users/example/Desktop/file.txt as the filename. This gets me really confused. That being said, are macros FILENAME_MAX and PATH_MAX the same?

FILENAME_MAX is defined in stdio.h or something so it's cross platform, but am I giving my self extra work doing this?

#ifdef __linux__
#   include <linux/limits.h>
#elif defined(_WIN32)
#   include <windows.h>
#   define PATH_MAX MAX_PATH
#else
#   include <sys/syslimits.h>
#endif

When just #include <stdio.h> and using FILENAME_MAX is enough? To create path buffers large enough to hold any file path?

Upvotes: 6

Views: 10967

Answers (5)

Aconcagua
Aconcagua

Reputation: 25536

Late addition for late readers: As common sense from the other answers is that one cannot trust either of – my variant of giving back the trust (a little of, at least) is using the minimum of both and 1024. This way we have a reasonable upper limit e.g. for arrays storing file paths and still consider potentially stricter limits imposed by the OS:

#if defined _WIN32 || defined _WIN64
#define PATH_MAX MAX_PATH
#elif not defined PATH_MAX
#define PATH_MAX FILENAME_MAX
#endif

#define MY_PATH_MAX (PATH_MAX > 1024 && FILENAME_MAX > 1024 ? 1024 : (PATH_MAX < FILENAME_MAX ? PATH_MAX : FILENAME_MAX))

// more convenient in C++:
size_t constexpr PathMax = std::min({1024, PATH_MAX, FILENAME_MAX});

Problems with this limit being smaller than path sizes actually allowed by the OS or still too large if creating paths on media to be read on other OS with stricter limits aren't mitigated this way, though.

Upvotes: 1

Myst
Myst

Reputation: 19221

It is often (quite arbitrarily) set so PATH_MAX equals 4096 and NAME_MAX (FILENAME_MAX?) would be 255, as mentioned here.

This should be considered legacy in practice due to possible some file names could end up being longer than PATH_MAX / FILENAME_MAX due to different ways path name lengths, path name concatenation and path name expansion could be performed (not to mention differences in string encodings).

In addition, sometimes the defined PATH_MAX value has little or nothing to do with actual limits (if any) imposed by the OS.

For a long time (maybe still), Windows systems defined PATH_MAX as 260, while the OS supported path names up to 32Kb or more (there are some details here).

Upvotes: 3

Luis Colorado
Luis Colorado

Reputation: 12698

PATH_MAX is normally a fixed constant (for posix systems) that specify the maximum length of the path parameter you pass to the kernel in a system call (e.g. open(2) syscall)

But the maximum path length a file can have, is not affected by this constant, as you can test with this simple shell scritp:

$ i=0
> while [ "$i" -lt 2000 ]
> do mkdir "$i"
>    cd "$i"
>    i=`expr "$i" + 1`
> done
$ pwd
/home/lcu/0/1/2/3/4/5/6/7/8/9/[intermediate output not shown...]/1996/1997/1998/1999

Now, if you try:

$ cd `pwd`
-bash: cd: /home/lcu/0/1/2/3/4/5/6/7/8/9/[intermediate output not shown...]/1996/1997/1998/1999: File name too long

Showing you that the problem is when bash tries to do the chdir(2) system call. (there's a little routine below that shows you how to open a file whose name is longer than the PATH_MAX constant)

You will end in a directory that has many more characters in it's name than the PATH_MAX constant. That doesn't mean you cannot access that file from the root directory, but you have to do it

  • changing your curren directory (as the script does)
  • using the openat(2) and friends to use a closer opend directory as start point to access it. The absolute number of path elements a file can have to the root node is limited only by the number of inodes in the filesystem, and the total capacity of it.

Edit

When just #include <stdio.h> and using FILENAME_MAX is enough? To create path buffers large enough to hold any file path?

The only way to support any file length, for opening a file in a POSIX operating system and be portable at the same time, is to divide your path in short enough chunks of path, and do n-1 chdir(2) to the actual place you have your files on. Then call your open(2) system call with the last chunk, and then return to the directory you where, if that's possible, with the fchdir(2) system call (if your system has it). In case you have the openat(2) system call, you can just open the directories (with the opendir() call) closing the previous (as you only use it to open the next dir) and to get close enough to the final directory to be able to open it with an openat(2) system call.

Below is a (work in progress) myfopen() call that tries to open a file longer thatn the PATH_MAX limit, from <limits.h>:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "myfopen.h"

FILE *my_fopen(const char *filename, const char *mode)
{
    if (strlen(filename) > PATH_MAX) {
        char *work_name = strdup(filename);
        if (!work_name) {
            return NULL; /* cannot malloc */
        }
        char *to_free   = work_name;  /* to free at end */
        int dir_fd      = -1;
        while(strlen(work_name) >= PATH_MAX) {
            char *p = work_name + PATH_MAX - 1;
            while(*p != '/') p--;
            /* p points to the previous '/' to the PATH_MAX limit */
            *p++ = '\0';
            if (dir_fd < 0) {
                dir_fd = open(work_name, 0);
                if (dir_fd < 0) {
                    ERR("open: %s: %s\n", work_name,
                            strerror(errno));
                    free(to_free);
                    return NULL;
                }
            } else {
                int aux_fd = openat(dir_fd, work_name, 0);
                close(dir_fd);
                if (aux_fd < 0) {
                    ERR("openat: %s: %s\n", work_name,
                        strerror(errno));
                    free(to_free);
                    return NULL;
                }
                dir_fd = aux_fd;
            }
            work_name = p;
        }
        /* strlen(work_name) < PATH_MAX,
         * work_name points to the last chunk and
         * dir_fd is the directory to base the real fopen
         */
        int fd = openat(
                dir_fd,
                work_name,
                O_RDONLY); /* this needs to be
                            * adjusted, based on what is
                            * specified in string mode */
        close(dir_fd);
        if (fd < 0) {
            fprintf(stderr, "openat: %s: %s\n",
                    work_name, strerror(errno));
            free(to_free);
            return NULL;
        }
        free(to_free);
        return fdopen(fd, mode);
    }
    return fopen(filename, mode);
}

(you will need to work the appropiate set of bit masks to pass to the final openat() call, in order to comply with the different ways of specifying the open mode of the fopen() vs. open() calls)

The basic problem with the maximum file name length (which is, why the kernel designers don't support an unbounded buffer to hold the full name of the file) is a security based one. If you allow a user to pass a 20Gb long filename into kernel space, probably you'll run out kernel memory space and that cannot be permitted (there should be a strong weakness in the system, as a malicious user could block the whole kernel, just passing a very long filename)

In the normal case, I have never had to deal with files longer than 1024 bytes, except for demostrations of this specific problem, so IMHO you should accept that limit, and procure to use short filenames (shorter than 1024 is a good limit)

On other side, you are mentioning many constants here:

  • FILENAME_MAX is used by stdio system, but its value is to be used only with stdio routines. Its documentation states that:

This macro constant expands to an integral expression corresponding to the size needed for an array of char elements to hold the longest file name string allowed by the library. Or, if the library imposes no such restriction, it is set to the recommended size for character arrays intended to hold a file name.

This means that it's a secure length you can tie to in order to be able to call fopen(3) and pass it a working filename.

  • PATH_MAX is a secure value that you can use on your system probably. If you try to open files (with the open(2) system call) or to erase (unlink(2)), rename, etc. Probably you'll run in trouble if you try to do so with filenames longer than this.

The limitation on the file path length comes from a limitation imposed by the system call subsystem, and not from the filesystem involved on the operation, so normally the limitation will be for all files in the system, and not for a specific mounted filesystems (which can be further limited, or not)

In my opinion, the limits are well thought, and using the values published will be ok for the majority of cases. And no tool in the operating system allows you to open a file with a path longer than PATH_MAX.

Upvotes: 1

Joshua
Joshua

Reputation: 43317

These aren't comparable at all. The *nix constant PATH_MAX is the largest path you can pass to a system call on *nix operating systems but longer paths can exist because system calls take relative paths, while the Windows constant MAX_PATH was the largest possible absolute path that could occur on Windows. In fact MAX_PATH has been revoked by the ABI but you need to opt-in to using longer paths.

Upvotes: 1

Schwern
Schwern

Reputation: 165200

tl;dr Don't trust either of them.

A "path" is an absolute path like /home/you/foo.txt or a relative path like you/foo.txt or even foo.txt.

A "filename" is poorly defined. It could be just the "basename" foo.txt or it could be a synonym for "path". The C standard seems to use "filename" to mean "path". For example, fopen takes a filename which is really a path.

FILENAME_MAX is standard C, PATH_MAX is POSIX, MAX_PATH is Windows.

FILENAME_MAX is a C standard constant...

which expands to an integer constant expression that is the size needed for an array of char large enough to hold the longest file name string that the implementation guarantees can be opened.

PATH_MAX is not part of the C standard. It is a POSIX constant...

Maximum number of bytes in a pathname, including the terminating null character.

If the path is too long, POSIX functions should give an ENAMETOOLONG error, but not all compilers enforce this.

MAX_PATH is a Windows API constant...

In the Windows API (with some exceptions discussed in the following paragraphs), the maximum length for a path is MAX_PATH, which is defined as 260 characters. A local path is structured in the following order: drive letter, colon, backslash, name components separated by backslashes, and a terminating null character. For example, the maximum path on drive D is "D:\some 256-character path string"

For example, C standard fopen uses FILENAME_MAX. POSIX open uses PATH_MAX.

But probably don't trust them.

FILENAME_MAX is not safe to use to allocate memory because it might be INT_MAX. The GCC documentation warns...

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! Use dynamic allocation (see Memory Allocation) instead.

PATH_MAX may not be defined.

Because they are hard coded by the operating system, neither can be trusted to be a true representation of what the file system can handle. For example, my Mac defines both PATH_MAX and FILENAME_MAX as 1024. A FAT32 filesystem has a limit of 255 characters. If I mount a FAT32 filesystem on my Mac, PATH_MAX and FILENAME_MAX will be incorrect.

Evan Klitzke describes the problems with PATH_MAX and by extension FILENAME_MAX.

If a function requires you to allocate a buffer to store a path, there is often an alternative which will allocate memory for you, or which will take a max size. If you're left with no choice, 1024 or 4096 are good choices.

Upvotes: 4

Related Questions