dirut.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621
  1. #include "dirut.h"
  2. #include "iterator.h"
  3. #include "filelist.h"
  4. #include "fts.h"
  5. #include "pathsplit.h"
  6. #include "path.h"
  7. #include <util/generic/yexception.h>
  8. #include <util/system/compiler.h>
  9. #include <util/system/fs.h>
  10. #include <util/system/maxlen.h>
  11. #include <util/system/yassert.h>
  12. void SlashFolderLocal(TString& folder) {
  13. if (!folder)
  14. return;
  15. #ifdef _win32_
  16. size_t pos;
  17. while ((pos = folder.find('/')) != TString::npos)
  18. folder.replace(pos, 1, LOCSLASH_S);
  19. #endif
  20. if (folder[folder.size() - 1] != LOCSLASH_C)
  21. folder.append(LOCSLASH_S);
  22. }
  23. #ifndef _win32_
  24. bool correctpath(TString& folder) {
  25. return resolvepath(folder, "/");
  26. }
  27. bool resolvepath(TString& folder, const TString& home) {
  28. Y_ASSERT(home && home.at(0) == '/');
  29. if (!folder) {
  30. return false;
  31. }
  32. // may be from windows
  33. char* ptr = folder.begin();
  34. while ((ptr = strchr(ptr, '\\')) != nullptr)
  35. *ptr = '/';
  36. if (folder.at(0) == '~') {
  37. if (folder.length() == 1 || folder.at(1) == '/') {
  38. folder = GetHomeDir() + (folder.data() + 1);
  39. } else {
  40. char* buf = (char*)alloca(folder.length() + 1);
  41. strcpy(buf, folder.data() + 1);
  42. char* p = strchr(buf, '/');
  43. if (p)
  44. *p++ = 0;
  45. passwd* pw = getpwnam(buf);
  46. if (pw) {
  47. folder = pw->pw_dir;
  48. folder += "/";
  49. if (p)
  50. folder += p;
  51. } else {
  52. return false; // unknown user
  53. }
  54. }
  55. }
  56. int len = folder.length() + home.length() + 1;
  57. char* path = (char*)alloca(len);
  58. if (folder.at(0) != '/') {
  59. strcpy(path, home.data());
  60. strcpy(strrchr(path, '/') + 1, folder.data()); // the last char must be '/' if it's a dir
  61. } else {
  62. strcpy(path, folder.data());
  63. }
  64. len = strlen(path) + 1;
  65. // grabbed from url.cpp
  66. char* newpath = (char*)alloca(len + 2);
  67. const char** pp = (const char**)alloca(len * sizeof(char*));
  68. int i = 0;
  69. for (char* s = path; s;) {
  70. pp[i++] = s;
  71. s = strchr(s, '/');
  72. if (s)
  73. *s++ = 0;
  74. }
  75. for (int j = 1; j < i;) {
  76. const char*& p = pp[j];
  77. if (strcmp(p, ".") == 0 || strcmp(p, "") == 0) {
  78. if (j == i - 1) {
  79. p = "";
  80. break;
  81. } else {
  82. memmove(pp + j, pp + j + 1, (i - j - 1) * sizeof(p));
  83. --i;
  84. }
  85. } else if (strcmp(p, "..") == 0) {
  86. if (j == i - 1) {
  87. if (j == 1) {
  88. p = "";
  89. } else {
  90. --i;
  91. pp[j - 1] = "";
  92. }
  93. break;
  94. } else {
  95. if (j == 1) {
  96. memmove(pp + j, pp + j + 1, (i - j - 1) * sizeof(p));
  97. --i;
  98. } else {
  99. memmove(pp + j - 1, pp + j + 1, (i - j - 1) * sizeof(p));
  100. i -= 2;
  101. --j;
  102. }
  103. }
  104. } else
  105. ++j;
  106. }
  107. char* s = newpath;
  108. for (int k = 0; k < i; k++) {
  109. s = strchr(strcpy(s, pp[k]), 0);
  110. *s++ = '/';
  111. }
  112. *(--s) = 0;
  113. folder = newpath;
  114. return true;
  115. }
  116. #else
  117. using dir_type = enum {
  118. dt_empty,
  119. dt_error,
  120. dt_up,
  121. dt_dir
  122. };
  123. // precondition: *ptr != '\\' || *ptr == 0 (cause dt_error)
  124. // postcondition: *ptr != '\\'
  125. template <typename T>
  126. static int next_dir(T*& ptr) {
  127. int has_blank = 0;
  128. int has_dot = 0;
  129. int has_letter = 0;
  130. int has_ctrl = 0;
  131. while (*ptr && *ptr != '\\') {
  132. int c = (unsigned char)*ptr++;
  133. switch (c) {
  134. case ' ':
  135. ++has_blank;
  136. break;
  137. case '.':
  138. ++has_dot;
  139. break;
  140. case '/':
  141. case ':':
  142. case '*':
  143. case '?':
  144. case '"':
  145. case '<':
  146. case '>':
  147. case '|':
  148. ++has_ctrl;
  149. break;
  150. default:
  151. if (c == 127 || c < ' ')
  152. ++has_ctrl;
  153. else
  154. ++has_letter;
  155. }
  156. }
  157. if (*ptr)
  158. ++ptr;
  159. if (has_ctrl)
  160. return dt_error;
  161. if (has_letter)
  162. return dt_dir;
  163. if (has_dot && has_blank)
  164. return dt_error;
  165. if (has_dot == 1)
  166. return dt_empty;
  167. if (has_dot == 2)
  168. return dt_up;
  169. return dt_error;
  170. }
  171. using disk_type = enum {
  172. dk_noflags = 0,
  173. dk_unc = 1,
  174. dk_hasdrive = 2,
  175. dk_fromroot = 4,
  176. dk_error = 8
  177. };
  178. // root slash (if any) - part of disk
  179. template <typename T>
  180. static int skip_disk(T*& ptr) {
  181. int result = dk_noflags;
  182. if (!*ptr)
  183. return result;
  184. if (ptr[0] == '\\' && ptr[1] == '\\') {
  185. result |= dk_unc | dk_fromroot;
  186. ptr += 2;
  187. if (next_dir(ptr) != dt_dir)
  188. return dk_error; // has no host name
  189. if (next_dir(ptr) != dt_dir)
  190. return dk_error; // has no share name
  191. } else {
  192. if (*ptr && *(ptr + 1) == ':') {
  193. result |= dk_hasdrive;
  194. ptr += 2;
  195. }
  196. if (*ptr == '\\' || *ptr == '/') {
  197. ++ptr;
  198. result |= dk_fromroot;
  199. }
  200. }
  201. return result;
  202. }
  203. int correctpath(char* cpath, const char* path) {
  204. if (!path || !*path) {
  205. *cpath = 0;
  206. return 1;
  207. }
  208. char* ptr = (char*)path;
  209. char* cptr = cpath;
  210. int counter = 0;
  211. while (*ptr) {
  212. char c = *ptr;
  213. if (c == '/')
  214. c = '\\';
  215. if (c == '\\')
  216. ++counter;
  217. else
  218. counter = 0;
  219. if (counter <= 1) {
  220. *cptr = c;
  221. ++cptr;
  222. }
  223. ++ptr;
  224. }
  225. *cptr = 0;
  226. // replace '/' by '\'
  227. int dk = skip_disk(cpath);
  228. if (dk == dk_error)
  229. return 0;
  230. char* ptr1 = ptr = cpath;
  231. int level = 0;
  232. while (*ptr) {
  233. switch (next_dir(ptr)) {
  234. case dt_dir:
  235. ++level;
  236. break;
  237. case dt_empty:
  238. memmove(ptr1, ptr, strlen(ptr) + 1);
  239. ptr = ptr1;
  240. break;
  241. case dt_up:
  242. --level;
  243. if (level >= 0) {
  244. *--ptr1 = 0;
  245. ptr1 = strrchr(cpath, '\\');
  246. if (!ptr1)
  247. ptr1 = cpath;
  248. else
  249. ++ptr1;
  250. memmove(ptr1, ptr, strlen(ptr) + 1);
  251. ptr = ptr1;
  252. break;
  253. } else if (level == -1 && (dk & dk_hasdrive)) {
  254. if (*ptr && *(ptr + 1) == ':' && *(cpath - 2) == ':') {
  255. memmove(cpath - 3, ptr, strlen(ptr) + 1);
  256. return 1;
  257. }
  258. }
  259. if (dk & dk_fromroot)
  260. return 0;
  261. break;
  262. case dt_error:
  263. default:
  264. return 0;
  265. }
  266. ptr1 = ptr;
  267. }
  268. if ((ptr > cpath || ptr == cpath && dk & dk_unc) && *(ptr - 1) == '\\')
  269. *(ptr - 1) = 0;
  270. return 1;
  271. }
  272. static inline int normchar(unsigned char c) {
  273. return (c < 'a' || c > 'z') ? c : c - 32;
  274. }
  275. static inline char* strslashcat(char* a, const char* b) {
  276. size_t len = strlen(a);
  277. if (len && a[len - 1] != '\\')
  278. a[len++] = '\\';
  279. strcpy(a + len, b);
  280. return a;
  281. }
  282. int resolvepath(char* apath, const char* rpath, const char* cpath) {
  283. const char* redisk = rpath;
  284. if (!rpath || !*rpath)
  285. return 0;
  286. int rdt = skip_disk(redisk);
  287. if (rdt == dk_error)
  288. return 0;
  289. if (rdt & dk_unc || rdt & dk_hasdrive && rdt & dk_fromroot) {
  290. return correctpath(apath, rpath);
  291. }
  292. const char* cedisk = cpath;
  293. if (!cpath || !*cpath)
  294. return 0;
  295. int cdt = skip_disk(cedisk);
  296. if (cdt == dk_error)
  297. return 0;
  298. char* tpath = (char*)alloca(strlen(rpath) + strlen(cpath) + 3);
  299. // rdt&dk_hasdrive && !rdt&dk_fromroot
  300. if (rdt & dk_hasdrive) {
  301. if (!(cdt & dk_fromroot))
  302. return 0;
  303. if (cdt & dk_hasdrive && normchar(*rpath) != normchar(*cpath))
  304. return 0;
  305. memcpy(tpath, rpath, 2);
  306. memcpy(tpath + 2, cedisk, strlen(cedisk) + 1);
  307. strslashcat(tpath, redisk);
  308. // !rdt&dk_hasdrive && rdt&dk_fromroot
  309. } else if (rdt & dk_fromroot) {
  310. if (!(cdt & dk_hasdrive) && !(cdt & dk_unc))
  311. return 0;
  312. memcpy(tpath, cpath, cedisk - cpath);
  313. tpath[cedisk - cpath] = 0;
  314. strslashcat(tpath, redisk);
  315. // !rdt&dk_hasdrive && !rdt&dk_fromroot
  316. } else {
  317. if (!(cdt & dk_fromroot) || !(cdt & dk_hasdrive) && !(cdt & dk_unc))
  318. return 0;
  319. strslashcat(strcpy(tpath, cpath), redisk);
  320. }
  321. return correctpath(apath, tpath);
  322. }
  323. bool correctpath(TString& filename) {
  324. char* ptr = (char*)alloca(filename.size() + 2);
  325. if (correctpath(ptr, filename.data())) {
  326. filename = ptr;
  327. return true;
  328. }
  329. return false;
  330. }
  331. bool resolvepath(TString& folder, const TString& home) {
  332. char* ptr = (char*)alloca(folder.size() + 3 + home.size());
  333. if (resolvepath(ptr, folder.data(), home.data())) {
  334. folder = ptr;
  335. return true;
  336. }
  337. return false;
  338. }
  339. #endif // !defined _win32_
  340. char GetDirectorySeparator() {
  341. return LOCSLASH_C;
  342. }
  343. const char* GetDirectorySeparatorS() {
  344. return LOCSLASH_S;
  345. }
  346. void RemoveDirWithContents(TString dirName) {
  347. SlashFolderLocal(dirName);
  348. TDirIterator dir(dirName, TDirIterator::TOptions(FTS_NOSTAT));
  349. for (auto it = dir.begin(); it != dir.end(); ++it) {
  350. switch (it->fts_info) {
  351. case FTS_F:
  352. case FTS_DEFAULT:
  353. case FTS_DP:
  354. case FTS_SL:
  355. case FTS_SLNONE:
  356. if (!NFs::Remove(it->fts_path))
  357. ythrow TSystemError() << "error while removing " << it->fts_path;
  358. break;
  359. }
  360. }
  361. }
  362. int mkpath(char* path, int mode) {
  363. return NFs::MakeDirectoryRecursive(path, NFs::EFilePermission(mode)) ? 0 : -1;
  364. }
  365. // Implementation of realpath in FreeBSD (version 9.0 and less) and GetFullPathName in Windows
  366. // did not require last component of the file name to exist (other implementations will fail
  367. // if it does not). Use RealLocation if that behaviour is required.
  368. TString RealPath(const TString& path) {
  369. TTempBuf result;
  370. Y_ASSERT(result.Size() > MAX_PATH); // TMP_BUF_LEN > MAX_PATH
  371. #ifdef _win_
  372. if (GetFullPathName(path.data(), result.Size(), result.Data(), nullptr) == 0)
  373. #else
  374. if (realpath(path.data(), result.Data()) == nullptr)
  375. #endif
  376. ythrow TFileError() << "RealPath failed \"" << path << "\"";
  377. return result.Data();
  378. }
  379. TString RealLocation(const TString& path) {
  380. if (NFs::Exists(path))
  381. return RealPath(path);
  382. TString dirpath = GetDirName(path);
  383. if (NFs::Exists(dirpath))
  384. return RealPath(dirpath) + GetDirectorySeparatorS() + GetFileNameComponent(path.data());
  385. ythrow TFileError() << "RealLocation failed \"" << path << "\"";
  386. }
  387. int MakeTempDir(char path[/*FILENAME_MAX*/], const char* prefix) {
  388. int ret;
  389. TString sysTmp;
  390. #ifdef _win32_
  391. if (!prefix || *prefix == '/') {
  392. #else
  393. if (!prefix) {
  394. #endif
  395. sysTmp = GetSystemTempDir();
  396. prefix = sysTmp.data();
  397. }
  398. if ((ret = ResolvePath(prefix, nullptr, path, 1)) != 0)
  399. return ret;
  400. if (!TFileStat(path).IsDir())
  401. return ENOENT;
  402. if ((strlcat(path, "tmpXXXXXX", FILENAME_MAX) > FILENAME_MAX - 100))
  403. return EINVAL;
  404. if (!(mkdtemp(path)))
  405. return errno ? errno : EINVAL;
  406. strcat(path, LOCSLASH_S);
  407. return 0;
  408. }
  409. bool IsDir(const TString& path) {
  410. return TFileStat(path).IsDir();
  411. }
  412. TString GetHomeDir() {
  413. TString s(getenv("HOME"));
  414. if (!s) {
  415. #ifndef _win32_
  416. passwd* pw = nullptr;
  417. s = getenv("USER");
  418. if (s)
  419. pw = getpwnam(s.data());
  420. else
  421. pw = getpwuid(getuid());
  422. if (pw)
  423. s = pw->pw_dir;
  424. else
  425. #endif
  426. {
  427. char* cur_dir = getcwd(nullptr, 0);
  428. s = cur_dir;
  429. free(cur_dir);
  430. }
  431. }
  432. return s;
  433. }
  434. void MakeDirIfNotExist(const char* path, int mode) {
  435. if (!NFs::MakeDirectory(path, NFs::EFilePermission(mode)) && !NFs::Exists(path)) {
  436. ythrow TSystemError() << "failed to create directory " << path;
  437. }
  438. }
  439. void MakePathIfNotExist(const char* path, int mode) {
  440. NFs::MakeDirectoryRecursive(path, NFs::EFilePermission(mode));
  441. if (!NFs::Exists(path) || !TFileStat(path).IsDir()) {
  442. ythrow TSystemError() << "failed to create directory " << path;
  443. }
  444. }
  445. const char* GetFileNameComponent(const char* f) {
  446. const char* p = strrchr(f, LOCSLASH_C);
  447. #ifdef _win_
  448. // "/" is also valid char separator on Windows
  449. const char* p2 = strrchr(f, '/');
  450. if (p2 > p)
  451. p = p2;
  452. #endif
  453. if (p) {
  454. return p + 1;
  455. }
  456. return f;
  457. }
  458. TString GetSystemTempDir() {
  459. #ifdef _win_
  460. char buffer[1024];
  461. DWORD size = GetTempPath(1024, buffer);
  462. if (!size) {
  463. ythrow TSystemError() << "failed to get system temporary directory";
  464. }
  465. return TString(buffer, size);
  466. #else
  467. const char* var = "TMPDIR";
  468. const char* def = "/tmp";
  469. const char* r = getenv(var);
  470. const char* result = r ? r : def;
  471. return result[0] == '/' ? result : ResolveDir(result);
  472. #endif
  473. }
  474. TString ResolveDir(const char* path) {
  475. return ResolvePath(path, true);
  476. }
  477. bool SafeResolveDir(const char* path, TString& result) {
  478. try {
  479. result = ResolvePath(path, true);
  480. return true;
  481. } catch (...) {
  482. return false;
  483. }
  484. }
  485. TString GetDirName(const TString& path) {
  486. return TFsPath(path).Dirname();
  487. }
  488. #ifdef _win32_
  489. char* realpath(const char* pathname, char resolved_path[MAXPATHLEN]) {
  490. // partial implementation: no path existence check
  491. return _fullpath(resolved_path, pathname, MAXPATHLEN - 1);
  492. }
  493. #endif
  494. TString GetBaseName(const TString& path) {
  495. return TFsPath(path).Basename();
  496. }
  497. static bool IsAbsolutePath(const char* str) {
  498. return str && TPathSplitTraitsLocal::IsAbsolutePath(TStringBuf(str, NStringPrivate::GetStringLengthWithLimit(str, 3)));
  499. }
  500. int ResolvePath(const char* rel, const char* abs, char res[/*MAXPATHLEN*/], bool isdir) {
  501. char t[MAXPATHLEN * 2 + 3];
  502. size_t len;
  503. *res = 0;
  504. if (!rel || !*rel)
  505. return EINVAL;
  506. if (!IsAbsolutePath(rel) && IsAbsolutePath(abs)) {
  507. len = strlcpy(t, abs, sizeof(t));
  508. if (len >= sizeof(t) - 3)
  509. return EINVAL;
  510. if (t[len - 1] != LOCSLASH_C)
  511. t[len++] = LOCSLASH_C;
  512. len += strlcpy(t + len, rel, sizeof(t) - len);
  513. } else {
  514. len = strlcpy(t, rel, sizeof(t));
  515. }
  516. if (len >= sizeof(t) - 3)
  517. return EINVAL;
  518. if (isdir && t[len - 1] != LOCSLASH_C) {
  519. t[len++] = LOCSLASH_C;
  520. t[len] = 0;
  521. }
  522. if (!realpath(t, res)) {
  523. if (!isdir && realpath(GetDirName(t).data(), res)) {
  524. len = strlen(res);
  525. if (res[len - 1] != LOCSLASH_C) {
  526. res[len++] = LOCSLASH_C;
  527. res[len] = 0;
  528. }
  529. strcpy(res + len, GetBaseName(t).data());
  530. return 0;
  531. }
  532. return errno ? errno : ENOENT;
  533. }
  534. if (isdir) {
  535. len = strlen(res);
  536. if (res[len - 1] != LOCSLASH_C) {
  537. res[len++] = LOCSLASH_C;
  538. res[len] = 0;
  539. }
  540. }
  541. return 0;
  542. }
  543. TString ResolvePath(const char* rel, const char* abs, bool isdir) {
  544. char buf[PATH_MAX];
  545. if (ResolvePath(rel, abs, buf, isdir))
  546. ythrow yexception() << "cannot resolve path: \"" << rel << "\"";
  547. return buf;
  548. }
  549. TString ResolvePath(const char* path, bool isDir) {
  550. return ResolvePath(path, nullptr, isDir);
  551. }
  552. TString StripFileComponent(const TString& fileName) {
  553. TString dir = IsDir(fileName) ? fileName : GetDirName(fileName);
  554. if (!dir.empty() && dir.back() != GetDirectorySeparator()) {
  555. dir.append(GetDirectorySeparator());
  556. }
  557. return dir;
  558. }