1 /* getcwd() - get the name of the current working directory. 2 * Author: Kees J. Bot 3 * 30 Apr 1989 4 */ 5 6 #include <sys/cdefs.h> 7 #include "namespace.h" 8 9 #include <sys/types.h> 10 #include <sys/stat.h> 11 #include <errno.h> 12 #include <unistd.h> 13 #include <dirent.h> 14 #include <limits.h> 15 #include <string.h> 16 17 /* libc-private interface */ 18 int __getcwd(char *, size_t); 19 20 static int addpath(const char *path, char **ap, const char *entry) 21 /* Add the name of a directory entry at the front of the path being built. 22 * Note that the result always starts with a slash. 23 */ 24 { 25 const char *e= entry; 26 char *p= *ap; 27 28 while (*e != 0) e++; 29 30 while (e > entry && p > path) *--p = *--e; 31 32 if (p == path) return -1; 33 *--p = '/'; 34 *ap= p; 35 return 0; 36 } 37 38 static int recover(char *p) 39 /* Undo all those chdir("..")'s that have been recorded by addpath. This 40 * has to be done entry by entry, because the whole pathname may be too long. 41 */ 42 { 43 int e= errno, slash; 44 char *p0; 45 46 while (*p != 0) { 47 p0= ++p; 48 49 do p++; while (*p != 0 && *p != '/'); 50 slash= *p; *p= 0; 51 52 if (chdir(p0) < 0) return -1; 53 *p= slash; 54 } 55 errno= e; 56 return 0; 57 } 58 59 int __getcwd(char *path, size_t size) 60 { 61 struct stat above, current, tmp; 62 struct dirent *entry; 63 DIR *d; 64 char *p, *up; 65 const char *dotdot = ".."; 66 int cycle; 67 68 if (path == NULL || size <= 1) { errno= EINVAL; return -1; } 69 70 p= path + size; 71 *--p = 0; 72 73 if (stat(".", ¤t) < 0) return -1; 74 75 while (1) { 76 if (stat(dotdot, &above) < 0) { recover(p); return -1; } 77 78 if (above.st_dev == current.st_dev 79 && above.st_ino == current.st_ino) 80 break; /* Root dir found */ 81 82 if ((d= opendir(dotdot)) == NULL) { recover(p); return -1; } 83 84 /* Cycle is 0 for a simple inode nr search, or 1 for a search 85 * for inode *and* device nr. 86 */ 87 cycle= above.st_dev == current.st_dev ? 0 : 1; 88 89 do { 90 char name[3 + NAME_MAX + 1]; 91 92 tmp.st_ino= 0; 93 if ((entry= readdir(d)) == NULL) { 94 switch (++cycle) { 95 case 1: 96 rewinddir(d); 97 continue; 98 case 2: 99 closedir(d); 100 errno= ENOENT; 101 recover(p); 102 return -1; 103 } 104 } 105 if (strcmp(entry->d_name, ".") == 0) continue; 106 if (strcmp(entry->d_name, "..") == 0) continue; 107 108 switch (cycle) { 109 case 0: 110 /* Simple test on inode nr. */ 111 if (entry->d_ino != current.st_ino) continue; 112 /*FALL THROUGH*/ 113 114 case 1: 115 /* Current is mounted. */ 116 strcpy(name, "../"); 117 strcpy(name+3, entry->d_name); 118 if (stat(name, &tmp) < 0) continue; 119 break; 120 } 121 } while (tmp.st_ino != current.st_ino 122 || tmp.st_dev != current.st_dev); 123 124 up= p; 125 if (addpath(path, &up, entry->d_name) < 0) { 126 closedir(d); 127 errno = ERANGE; 128 recover(p); 129 return -1; 130 } 131 closedir(d); 132 133 if (chdir(dotdot) < 0) { recover(p); return -1; } 134 p= up; 135 136 current= above; 137 } 138 if (recover(p) < 0) return -1; /* Undo all those chdir("..")'s. */ 139 if (*p == 0) *--p = '/'; /* Cwd is "/" if nothing added */ 140 if (p > path) strcpy(path, p); /* Move string to start of path. */ 141 return 0; 142 } 143