#include #include #include #include #include #include #include #include #include #include #include // A simple demonstration of why PATH_MAX cannot be used the way many people // (especially in SP, unfortunately) usually do. // Es folgt eine versuchte Erklaerung anhand der POSIX-Spezifikation, wie z.B. // in limits.h(7posix) (keine Garantie auf Richtigkeit). Die Erklaerung besteht // aus zwei Teilen/Moeglichkeiten, auf die ich getrennt eingehe. // Der erste Teil: "Maximum number of bytes the implementation will store as a // pathname in a user-supplied buffer of unspecified size, including the // terminating null character." Heisst also, wenn man einer (POSIX-konformen // Implementierung von einer) Funktion einen Puffer fuer einen Pfad uebergibt, // dieser Funktion aber nicht sagt, wie lange der Puffer ist, wird die Funktion // davon ausgehen, dass der Puffer maximal PATH_MAX viele Bytes an Speicher // bereitstellt. Fuer manche Funktionen ist das so, aber die allermeisten // bekommen auch explizit die Groesse des Puffers uebergeben. Damit ist dieser // erste Teil nur historisch relevant. // Der zweite Teil: "Minimum number the implementation will accept as the // maximum number of bytes in a pathname." Das klingt schon interessanter, aber // auch verwirrend. Ich verstehe es so: wenn ich der Implementierung (z.B. // einer POSIX-konformen Bibliotheksfunktion, oder auch eines POSIX-konformen // Systemaufrufs) einen Pfad uebergebe, und dabei auch sage, wie lang dieser // Pfad ist, muss mindestens eine Laenge von PATH_MAX unterstuetzt werden, aber // nicht mehr. Auf Linux ist PATH_MAX ueblicherweise 4096, d.h. wenn ich dem // Linux-Kernel jetzt einen Pfad uebergebe, z.B. mit open(2), und dieser // laenger als 4096 Byte ist, darf der Linux-Kernel diesen mit einem Fehler // abweisen. Und dafuer ist in der Manpage von open(2) auch explizit der Fehler // "ENAMETOOLONG" aufgefuehrt: "pathname was too long." Laut der // POSIX-Spezifikation ist PATH_MAX ja aber eine "Minimum number [...]", d.h. // der Linux-Kernel duerfte auch laengere Pfade akzeptieren! Tut er aber // (vermutlich der Einfachheit halber) nicht. Wenn man laengere Pfade nutzen // will, muss man das ueber Umwege machen, wie z.B. in diesem Programm mit // chdir(2) und der Nutzung von relativen Pfaden. // Fazit: PATH_MAX hat grundsaetzlich gar nichts mit der maximal moeglichen // Laenge eines Dateipfades zu tun, und ist wenn ueberhaupt nur ein Minimum // (fuer ein Maximum), und kein Maximum, wie man beim Namen annehmen koennte. static noreturn void die(const char *msg) { perror(msg); exit(EXIT_FAILURE); } static size_t get_cwd_len(bool print) { size_t s = 4096; for (;;) { char buf[s]; if (getcwd(buf, sizeof(buf))) { if (print) { printf("Current working directory: %s\n", buf); } return strlen(buf); } if (errno != ERANGE) { die("getcwd"); } s *= 2; } } static volatile bool stop; static void handle(int _) { stop = true; } int main() { const size_t NAME_SIZE = 16; const mode_t DIR_MODE = 0755; const int DIR_FLAGS = O_RDONLY | O_DIRECTORY; char dir_name[NAME_SIZE + 1] = {}; for (size_t i = 0; i < NAME_SIZE; i += 4) { strcpy(dir_name + i, "abcd"); } struct sigaction sa = {.sa_handler = handle, .sa_flags = SA_RESTART}; sigemptyset(&sa.sa_mask); sigaction(SIGINT, &sa, NULL); int initial_dir = open(".", O_RDONLY); if (initial_dir == -1) { die("open(\".\")"); } if (mkdir(dir_name, DIR_MODE)) { die("initial mkdir"); } int fd = open(dir_name, DIR_FLAGS); if (fd == -1) { die("initial open"); } while (!stop) { printf("cwd length: %zu\n", get_cwd_len(false)); if (fchdir(fd)) { die("fchdir"); } if (mkdir(dir_name, DIR_MODE)) { die("mkdir"); } if (close(fd)) { die("close"); } fd = open(dir_name, DIR_FLAGS); if (fd == -1) { die("open"); } } size_t len = get_cwd_len(true); printf("PATH_MAX is %d, but the length of the cwd was %zu!\n", PATH_MAX, len); // Restore initial working directory to easily delete everything that was // created. if (fchdir(initial_dir)) { die("fchdir"); } char rm[] = "rm -r "; char cmd[sizeof(rm) + sizeof(dir_name)]; strcat(strcpy(cmd, rm), dir_name); if (system(cmd)) { die(cmd); } }