15

I just got burned by the 260 character path limitation in Windows. Why did Microsoft decided to limit paths to 3 characters for drive + 256 characters + 1 character for the terminator? Mac OS of the same vintage has a 31 character filename limit but no path limit since the FSSpec stored volume number, refnum, and 31 character filename (Pascal string as length byte + 31 characters). HFS running on 512k of RAM could support long paths, but Windows 3.1 and 95 cannot. Newer versions of Windows can under some limited circumstances break the 260 character path limit, but they are limited by compatibility to Win32 headers.

  • 5
    Probably because they used fixed sized buffers to store things like the current working directory and 256 is a nice round number in binary. – Ross Ridge Dec 10 '18 at 18:03
  • 3
    Maximum path size actually supported by NTFS is much bigger (32,000 characters according to the first answer of this question.) 260 character limit is imposed by Windows API any can be easily by-passed with 3rd party software like Total Commander. – wizofwor Dec 11 '18 at 8:41
  • 1
    The 32K limit only works with Unicode. For non-Unicode, the limit is 260. – cup Dec 11 '18 at 13:00
  • 3
    The 32K limit is available in Unicode only, because Windows (NT-based, not Windows 95-based) is a Unicode (ok, UCS-2) Operating System, with an ANSI (ok, ASCII plus various 8-bits extensions like CP and MBCS) backward-compatibility layer for functionality that existed in Windows 95-based OSes. Long Paths are a Windows NT-only feature, and so to use it you need to step out of the 8-bit character set compatibility layer. – Euro Micelli Dec 11 '18 at 17:41
  • 2
    Amazingly enough, the DOS LFN API (first exposed by Windows 95) theoretically supports paths longer than 260 characters... – Stephen Kitt Dec 11 '18 at 20:52
9

In the Windows world, the MAX_PATH 260-character limit dates back to the introduction of the Win32 APIs; it is for example documented in GetWindowsDirectory. Before that, Windows (at least in version 3) documented a 144-character limit; see for example GetSystemDirectory.

As far as why the path limit is 260 characters, the general answer you’ll find on the Internet is backwards compatibility. OK, that’s often the case in computing in general, and on Windows in particular (which is why many 30-year-old Windows programs can still be made to run on modern Windows, at least on 32-bit systems). The question then becomes, backwards compatibility with what?

The obvious answer would be DOS, and perhaps Win16. But DOS has a maximal path limit of 66 characters, constrained by its CDS (current directory structure) which has room for a 67-byte nul-terminated string to store each drive’s path (including drive letter, if appropriate). DOS-based versions of Windows couldn’t change this, since they had to maintain compatibility with DOS programs — imagine being able to store a file in a deeply-nested directory, only to have it be inaccessible from DOS! So the DOS limit doesn’t explain the 256-character limit in Win32. (I’m ignoring network drives here.)

(Incidentally, you can achieve the latter effect by mounting a FAT drive under Linux: Linux allows much longer paths on FAT than DOS or Windows can handle, so you can create directories which are so deeply nested that DOS can’t handle them properly.)

Digging further reveals something interesting; old Windows headers define, in stdlib.h,

#if defined(__OS2__) || defined(__WIN32__)
#define _MAX_PATH       260
#define _MAX_DRIVE      3
#define _MAX_DIR        256
#define _MAX_FNAME      256
#define _MAX_EXT        256
#else
#define _MAX_PATH       80
#define _MAX_DRIVE      3
#define _MAX_DIR        66
#define _MAX_FNAME      9
#define _MAX_EXT        5
#endif

The 80-byte MAX_PATH makes sense for DOS and Win16, based on the CDS above: that’s just enough room for a 66-byte path, \, 12-byte filename (11 bytes stored on disk and . separator), and a nul terminator.

The interesting part is the other definition: the famous 260-byte limit... defined for Win32, and for OS/2! Reading through the OS/2 APIs doesn’t help much, because they’re all designed to not have documented limits (programs are supposed to call DosQuerySysInfo with QSV_MAX_PATH_LENGTH to find the maximum path length), as are in fact most of the Windows APIs (e.g. GetCurrentDirectory which takes a buffer and allocated length, and indicates the required length if the buffer isn’t big enough). But Inside OS/2, in its description of the file system name space, says

All OS/2 filename and pathname interfaces, such as DosOpen, DosFindNext, and so on, are designed to take name strings of arbitrary length. Applications should use name buffers of at least 256 characters to ensure that a long name is not truncated.

So I get the impression that the 256-character limit is part of OS/2’s legacy, and that when it was chosen, 256 characters was considered sufficient for “name strings of arbitrary length”.

  • What I'd read for rationale early in the life of NT was that, though there was a pathname limit of 64K-1 bytes in the OS itself, it was deemed a requirement that application programmers could continue to allocate fixed-size string buffers, i.e., MAX_PATH needed to exist with some value. The specific value is just "seems reasonable". But that's NT when it became "Windows NT". I don't know whether the same limit applied while it was still "NT OS/2". – another-dave Dec 12 '18 at 13:08
  • I haven't read Windows header files in depth so the OS2 connection is news to me. I'm more familiar with the Unix and MacOS headers/interfaces which did not contain explicit limits on path length. However its worth pointing out that HFS did have a maximum filename length, but the data-structures to store file locations did not use paths at all. – Michael Shopsin Dec 12 '18 at 16:07
2

This answer is wrong.


The maximum file length was 8.3. Including the path separator, that's 12 characters. That means you need to have 21 nested directories before you start to have problems. 260 bytes is good enough for anyone; this size is big enough.

But why 260? The first character has to be a drive letter, the second has to be a colon, the third has to be a \ and the last has to be a '\0' byte... leaving 256 characters of usable space. That's a nice round number! And INT21 AH=47 returns a path "not including a drive or initial backslash"[1] which, assuming that it also doesn't have a trailing NULL byte if the path is full length, would be exactly 64 bytes! (Oh dear; this isn't an explanation at all!) And, since DOS has a maximum of 32 drives, for which it stores the CWD separately,[2] that means that the path table takes up 2048 bytes of RAM. Given that you only have 640k conventional memory to work with, 2k is probably the highest they could go.

Ever since then, newer versions of DOS with more memory to spare, and then the DOS-based Windows, and then the NT Windows, all had to retain compatibility for programs that only allocated 260 bytes of RAM for the path, no matter where they got it from, because that's how long a path is. This answer is wrong.

  • Service 0x47 returns a path with at most 64 characters. This doesn’t explain where the 256-character limit came from... – Stephen Kitt Dec 12 '18 at 7:56
  • Also, the maximum size of the CDS is 88 bytes, so 32 drives take up 2816 bytes. Under Windows that goes up since Windows maintains multiple CDS lists (each VM can have a different current directory on any given drive). – Stephen Kitt Dec 12 '18 at 9:37
  • @StephenKitt You're right; I was being daft. Though... what's that thing about the CDS? I don't understand what you mean by that. Why 88? – wizzwizz4 Dec 12 '18 at 18:57
  • 1
    The CDS (current directory structure) is the data structure which DOS uses to keep track of the current directory on each drive, along with a few other features. Each entry occupies 88 bytes on DOS 4 and later. It’s described in detail in Undocumented DOS. – Stephen Kitt Dec 12 '18 at 19:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.