fs.c (40297B)
1 // fs.c -- filesystem access 2 #include <assert.h> 3 #include <errno.h> 4 #include <fcntl.h> 5 #include <stdbool.h> 6 #include <stddef.h> 7 #include <stdint.h> 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <string.h> 11 #include <sys/stat.h> 12 #include <sys/types.h> 13 #include <uv.h> 14 15 #ifdef MSWIN 16 # include <io.h> 17 # include <shlobj.h> 18 #endif 19 20 #include "auto/config.h" 21 #include "nvim/os/fs.h" 22 #include "nvim/os/os_defs.h" 23 24 #if defined(HAVE_ACL) 25 # ifdef HAVE_SYS_ACL_H 26 # include <sys/acl.h> 27 # endif 28 # ifdef HAVE_SYS_ACCESS_H 29 # include <sys/access.h> 30 # endif 31 #endif 32 33 #ifdef HAVE_XATTR 34 # include <sys/xattr.h> 35 #endif 36 37 #include "nvim/api/private/helpers.h" 38 #include "nvim/ascii_defs.h" 39 #include "nvim/errors.h" 40 #include "nvim/gettext_defs.h" 41 #include "nvim/globals.h" 42 #include "nvim/log.h" 43 #include "nvim/macros_defs.h" 44 #include "nvim/memory.h" 45 #include "nvim/message.h" 46 #include "nvim/option_vars.h" 47 #include "nvim/os/os.h" 48 #include "nvim/path.h" 49 #include "nvim/types_defs.h" 50 #include "nvim/ui.h" 51 #include "nvim/vim_defs.h" 52 53 #ifdef HAVE_SYS_UIO_H 54 # include <sys/uio.h> 55 #endif 56 57 #ifdef MSWIN 58 # include "nvim/mbyte.h" 59 # include "nvim/option.h" 60 # include "nvim/os/os_win_console.h" 61 # include "nvim/strings.h" 62 #endif 63 64 #include "os/fs.c.generated.h" 65 66 #ifdef HAVE_XATTR 67 static const char e_xattr_erange[] 68 = N_("E1506: Buffer too small to copy xattr value or key"); 69 static const char e_xattr_e2big[] 70 = N_("E1508: Size of the extended attribute value is larger than the maximum size allowed"); 71 static const char e_xattr_other[] 72 = N_("E1509: Error occurred when reading or writing extended attribute"); 73 #endif 74 75 #define RUN_UV_FS_FUNC(ret, func, ...) \ 76 do { \ 77 uv_fs_t req; \ 78 ret = func(NULL, &req, __VA_ARGS__); \ 79 uv_fs_req_cleanup(&req); \ 80 } while (0) 81 82 // Many fs functions from libuv return that value on success. 83 static const int kLibuvSuccess = 0; 84 85 /// Changes the current directory to `path`. 86 /// 87 /// @return 0 on success, or negative error code. 88 int os_chdir(const char *path) 89 FUNC_ATTR_NONNULL_ALL 90 { 91 if (p_verbose >= 5) { 92 verbose_enter(); 93 smsg(0, "chdir(%s)", path); 94 verbose_leave(); 95 } 96 int err = uv_chdir(path); 97 if (err == 0) { 98 ui_call_chdir(cstr_as_string(path)); 99 } 100 return err; 101 } 102 103 /// Get the name of current directory. 104 /// 105 /// @param buf Buffer to store the directory name. 106 /// @param len Length of `buf`. 107 /// @return `OK` for success, `FAIL` for failure. 108 int os_dirname(char *buf, size_t len) 109 FUNC_ATTR_NONNULL_ALL 110 { 111 int error_number; 112 if ((error_number = uv_cwd(buf, &len)) != kLibuvSuccess) { 113 xstrlcpy(buf, uv_strerror(error_number), len); 114 return FAIL; 115 } 116 return OK; 117 } 118 119 /// Check if the given path is a directory and not a symlink to a directory. 120 /// @return `true` if `name` is a directory and NOT a symlink to a directory. 121 /// `false` if `name` is not a directory or if an error occurred. 122 bool os_isrealdir(const char *name) 123 FUNC_ATTR_NONNULL_ALL 124 { 125 uv_fs_t request; 126 if (uv_fs_lstat(NULL, &request, name, NULL) != kLibuvSuccess) { 127 return false; 128 } 129 if (S_ISLNK(request.statbuf.st_mode)) { 130 return false; 131 } 132 return S_ISDIR(request.statbuf.st_mode); 133 } 134 135 /// Check if the given path exists and is a directory. 136 /// 137 /// @return `true` if `name` is a directory. 138 bool os_isdir(const char *name) 139 FUNC_ATTR_NONNULL_ALL 140 { 141 int32_t mode = os_getperm(name); 142 if (mode < 0) { 143 return false; 144 } 145 146 return S_ISDIR(mode); 147 } 148 149 /// Check what `name` is: 150 /// @return NODE_NORMAL: file or directory (or doesn't exist) 151 /// NODE_WRITABLE: writable device, socket, fifo, etc. 152 /// NODE_OTHER: non-writable things 153 int os_nodetype(const char *name) 154 FUNC_ATTR_NONNULL_ALL 155 { 156 #ifndef MSWIN // Unix 157 uv_stat_t statbuf; 158 if (0 != os_stat(name, &statbuf)) { 159 return NODE_NORMAL; // File doesn't exist. 160 } 161 // uv_handle_type does not distinguish BLK and DIR. 162 // Related: https://github.com/joyent/libuv/pull/1421 163 if (S_ISREG(statbuf.st_mode) || S_ISDIR(statbuf.st_mode)) { 164 return NODE_NORMAL; 165 } 166 if (S_ISBLK(statbuf.st_mode)) { // block device isn't writable 167 return NODE_OTHER; 168 } 169 // Everything else is writable? 170 // buf_write() expects NODE_WRITABLE for char device /dev/stderr. 171 return NODE_WRITABLE; 172 #else // Windows 173 // Edge case from Vim os_win32.c: 174 // We can't open a file with a name "\\.\con" or "\\.\prn", trying to read 175 // from it later will cause Vim to hang. Thus return NODE_WRITABLE here. 176 if (strncmp(name, "\\\\.\\", 4) == 0) { 177 return NODE_WRITABLE; 178 } 179 180 // Vim os_win32.c:mch_nodetype does (since 7.4.015): 181 // wn = enc_to_utf16(name, NULL); 182 // hFile = CreatFile(wn, ...) 183 // to get a HANDLE. Whereas libuv just calls _get_osfhandle() on the fd we 184 // give it. But uv_fs_open later calls fs__capture_path which does a similar 185 // utf8-to-utf16 dance and saves us the hassle. 186 187 // macOS: os_open(/dev/stderr) would return UV_EACCES. 188 int fd = os_open(name, O_RDONLY 189 # ifdef O_NONBLOCK 190 | O_NONBLOCK 191 # endif 192 , 0); 193 if (fd < 0) { // open() failed. 194 return NODE_NORMAL; 195 } 196 int guess = uv_guess_handle(fd); 197 if (close(fd) == -1) { 198 ELOG("close(%d) failed. name='%s'", fd, name); 199 } 200 201 switch (guess) { 202 case UV_TTY: // FILE_TYPE_CHAR 203 return NODE_WRITABLE; 204 case UV_FILE: // FILE_TYPE_DISK 205 return NODE_NORMAL; 206 case UV_NAMED_PIPE: // not handled explicitly in Vim os_win32.c 207 case UV_UDP: // unix only 208 case UV_TCP: // unix only 209 case UV_UNKNOWN_HANDLE: 210 default: 211 return NODE_OTHER; // Vim os_win32.c default 212 } 213 #endif 214 } 215 216 /// Gets the absolute path of the currently running executable. 217 /// May fail if procfs is missing. #6734 218 /// @see path_exepath 219 /// 220 /// @param[out] buffer Full path to the executable. 221 /// @param[in] size Size of `buffer`. 222 /// 223 /// @return 0 on success, or libuv error code. 224 int os_exepath(char *buffer, size_t *size) 225 FUNC_ATTR_NONNULL_ALL 226 { 227 return uv_exepath(buffer, size); 228 } 229 230 /// Checks if the file `name` is executable. 231 /// 232 /// @param[in] name Filename to check. 233 /// @param[out,allocated] abspath Returns resolved exe path, if not NULL. 234 /// @param[in] use_path Also search $PATH. 235 /// 236 /// @return true if `name` is executable and 237 /// - can be found in $PATH, 238 /// - is relative to current dir or 239 /// - is absolute. 240 /// 241 /// @return `false` otherwise. 242 bool os_can_exe(const char *name, char **abspath, bool use_path) 243 FUNC_ATTR_NONNULL_ARG(1) 244 { 245 if (!use_path || gettail_dir(name) != name) { 246 #ifdef MSWIN 247 return is_executable_ext(name, abspath); 248 #else 249 // Must have path separator, cannot execute files in the current directory. 250 return ((use_path || gettail_dir(name) != name) 251 && is_executable(name, abspath)); 252 #endif 253 return false; 254 } 255 256 return is_executable_in_path(name, abspath); 257 } 258 259 /// Returns true if `name` is an executable file. 260 /// 261 /// @param[in] name Filename to check. 262 /// @param[out,allocated] abspath Returns full exe path, if not NULL. 263 static bool is_executable(const char *name, char **abspath) 264 FUNC_ATTR_NONNULL_ARG(1) 265 { 266 int32_t mode = os_getperm(name); 267 268 if (mode < 0) { 269 return false; 270 } 271 272 #ifdef MSWIN 273 // Windows does not have exec bit; just check if the file exists and is not 274 // a directory. 275 const bool ok = S_ISREG(mode); 276 #else 277 int r = -1; 278 if (S_ISREG(mode)) { 279 RUN_UV_FS_FUNC(r, uv_fs_access, name, X_OK, NULL); 280 } 281 const bool ok = (r == 0); 282 #endif 283 if (ok && abspath != NULL) { 284 *abspath = save_abs_path(name); 285 } 286 return ok; 287 } 288 289 #ifdef MSWIN 290 /// Checks if file `name` is executable under any of these conditions: 291 /// - extension is in $PATHEXT and `name` is executable 292 /// - result of any $PATHEXT extension appended to `name` is executable 293 static bool is_executable_ext(const char *name, char **abspath) 294 FUNC_ATTR_NONNULL_ARG(1) 295 { 296 const bool is_unix_shell = strstr(path_tail(p_sh), "powershell") == NULL 297 && strstr(path_tail(p_sh), "pwsh") == NULL 298 && strstr(path_tail(p_sh), "sh") != NULL; 299 char *nameext = strrchr(name, '.'); 300 size_t nameext_len = nameext ? strlen(nameext) : 0; 301 xstrlcpy(os_buf, name, sizeof(os_buf)); 302 char *buf_end = xstrchrnul(os_buf, NUL); 303 const char *pathext = os_getenv_noalloc("PATHEXT"); 304 if (!pathext) { 305 pathext = ".com;.exe;.bat;.cmd"; 306 } 307 const char *ext = pathext; 308 while (*ext) { 309 // If $PATHEXT itself contains dot: 310 if (ext[0] == '.' && (ext[1] == NUL || ext[1] == ENV_SEPCHAR)) { 311 if (is_executable(name, abspath)) { 312 return true; 313 } 314 // Skip it. 315 ext++; 316 if (*ext) { 317 ext++; 318 } 319 continue; 320 } 321 322 const char *ext_end = ext; 323 size_t ext_len = 324 copy_option_part((char **)&ext_end, buf_end, 325 sizeof(os_buf) - (size_t)(buf_end - os_buf), ENV_SEPSTR); 326 if (ext_len != 0) { 327 bool in_pathext = nameext_len == ext_len 328 && 0 == mb_strnicmp(nameext, ext, ext_len); 329 330 if (((in_pathext || is_unix_shell) && is_executable(name, abspath)) 331 || is_executable(os_buf, abspath)) { 332 return true; 333 } 334 } 335 ext = ext_end; 336 } 337 return false; 338 } 339 #else 340 # define is_executable_ext is_executable 341 #endif 342 343 /// Checks if a file is in `$PATH` and is executable. 344 /// 345 /// @param[in] name Filename to check. 346 /// @param[out] abspath Returns resolved executable path, if not NULL. 347 /// 348 /// @return `true` if `name` is an executable inside `$PATH`. 349 static bool is_executable_in_path(const char *name, char **abspath) 350 FUNC_ATTR_NONNULL_ARG(1) 351 { 352 char *path_env = os_getenv("PATH"); 353 if (path_env == NULL) { 354 return false; 355 } 356 357 #ifdef MSWIN 358 char *path = NULL; 359 if (!os_env_exists("NoDefaultCurrentDirectoryInExePath", false) 360 && strstr(path_tail(p_sh), "cmd.exe") != NULL) { 361 // Prepend ".;" to $PATH. 362 size_t pathlen = strlen(path_env); 363 path = xmallocz(pathlen + 2); 364 memcpy(path, "." ENV_SEPSTR, 2); 365 memcpy(path + 2, path_env, pathlen); 366 } else { 367 path = xstrdup(path_env); 368 } 369 #else 370 char *path = xstrdup(path_env); 371 #endif 372 373 const size_t bufsize = strlen(name) + strlen(path) + 2; 374 char *buf = xmalloc(bufsize); 375 376 // Walk through all entries in $PATH to check if "name" exists there and 377 // is an executable file. 378 char *p = path; 379 bool rv = false; 380 while (true) { 381 char *e = xstrchrnul(p, ENV_SEPCHAR); 382 383 // Combine the $PATH segment with `name`. 384 xmemcpyz(buf, p, (size_t)(e - p)); 385 (void)append_path(buf, name, bufsize); 386 387 if (is_executable_ext(buf, abspath)) { 388 rv = true; 389 goto end; 390 } 391 392 if (*e != ENV_SEPCHAR) { 393 // End of $PATH without finding any executable called name. 394 goto end; 395 } 396 397 p = e + 1; 398 } 399 400 end: 401 xfree(buf); 402 xfree(path); 403 xfree(path_env); 404 return rv; 405 } 406 407 /// Opens or creates a file and returns a non-negative integer representing 408 /// the lowest-numbered unused file descriptor, for use in subsequent system 409 /// calls (read, write, lseek, fcntl, etc.). If the operation fails, a libuv 410 /// error code is returned, and no file is created or modified. 411 /// 412 /// @param path Filename 413 /// @param flags Bitwise OR of flags defined in <fcntl.h> 414 /// @param mode Permissions for the newly-created file (IGNORED if 'flags' is 415 /// not `O_CREAT` or `O_TMPFILE`), subject to the current umask 416 /// @return file descriptor, or negative error code on failure 417 int os_open(const char *path, int flags, int mode) 418 { 419 if (path == NULL) { // uv_fs_open asserts on NULL. #7561 420 return UV_EINVAL; 421 } 422 int r; 423 RUN_UV_FS_FUNC(r, uv_fs_open, path, flags, mode, NULL); 424 return r; 425 } 426 427 /// Compatibility wrapper conforming to fopen(3). 428 /// 429 /// Windows: works with UTF-16 filepaths by delegating to libuv (os_open). 430 /// 431 /// Future: remove this, migrate callers to os/fileio.c ? 432 /// But file_open_fd does not support O_RDWR yet. 433 /// 434 /// @param path Filename 435 /// @param flags String flags, one of { r w a r+ w+ a+ rb wb ab } 436 /// @return FILE pointer, or NULL on error. 437 FILE *os_fopen(const char *path, const char *flags) 438 { 439 assert(flags != NULL && strlen(flags) > 0 && strlen(flags) <= 2); 440 int iflags = 0; 441 // Per table in fopen(3) manpage. 442 if (flags[1] == NUL || flags[1] == 'b') { 443 switch (flags[0]) { 444 case 'r': 445 iflags = O_RDONLY; 446 break; 447 case 'w': 448 iflags = O_WRONLY | O_CREAT | O_TRUNC; 449 break; 450 case 'a': 451 iflags = O_WRONLY | O_CREAT | O_APPEND; 452 break; 453 default: 454 abort(); 455 } 456 #ifdef MSWIN 457 if (flags[1] == 'b') { 458 iflags |= O_BINARY; 459 } 460 #endif 461 } else { 462 // char 0 must be one of ('r','w','a'). 463 // char 1 is always '+' ('b' is handled above). 464 assert(flags[1] == '+'); 465 switch (flags[0]) { 466 case 'r': 467 iflags = O_RDWR; 468 break; 469 case 'w': 470 iflags = O_RDWR | O_CREAT | O_TRUNC; 471 break; 472 case 'a': 473 iflags = O_RDWR | O_CREAT | O_APPEND; 474 break; 475 default: 476 abort(); 477 } 478 } 479 // Per fopen(3) manpage: default to 0666, it will be umask-adjusted. 480 int fd = os_open(path, iflags, 0666); 481 if (fd < 0) { 482 return NULL; 483 } 484 return fdopen(fd, flags); 485 } 486 487 /// Sets file descriptor `fd` to close-on-exec (Unix) or non-inheritable (Windows). 488 /// 489 /// @return -1 if failed to set, 0 otherwise. 490 int os_set_cloexec(const int fd) 491 { 492 #ifdef HAVE_FD_CLOEXEC 493 int e; 494 int fdflags = fcntl(fd, F_GETFD); 495 if (fdflags < 0) { 496 e = errno; 497 ELOG("Failed to get flags on descriptor %d: %s", fd, strerror(e)); 498 errno = e; 499 return -1; 500 } 501 if ((fdflags & FD_CLOEXEC) == 0 502 && fcntl(fd, F_SETFD, fdflags | FD_CLOEXEC) == -1) { 503 e = errno; 504 ELOG("Failed to set CLOEXEC on descriptor %d: %s", fd, strerror(e)); 505 errno = e; 506 return -1; 507 } 508 return 0; 509 #elif defined(MSWIN) 510 HANDLE h = (HANDLE)_get_osfhandle(fd); 511 if (h == INVALID_HANDLE_VALUE 512 || !SetHandleInformation(h, HANDLE_FLAG_INHERIT, 0)) { 513 return -1; 514 } 515 return 0; 516 #else 517 return -1; 518 #endif 519 } 520 521 /// Close a file 522 /// 523 /// @return 0 or libuv error code on failure. 524 int os_close(const int fd) 525 { 526 int r; 527 RUN_UV_FS_FUNC(r, uv_fs_close, fd, NULL); 528 return r; 529 } 530 531 /// Duplicate file descriptor 532 /// 533 /// @param[in] fd File descriptor to duplicate. 534 /// 535 /// @return New file descriptor or libuv error code (< 0). 536 int os_dup(const int fd) 537 FUNC_ATTR_WARN_UNUSED_RESULT 538 { 539 int ret; 540 os_dup_dup: 541 ret = dup(fd); 542 if (ret < 0) { 543 const int error = os_translate_sys_error(errno); 544 errno = 0; 545 if (error == UV_EINTR) { 546 goto os_dup_dup; 547 } else { 548 return error; 549 } 550 } 551 return ret; 552 } 553 554 /// Open the file descriptor for stdin. 555 int os_open_stdin_fd(void) 556 { 557 int stdin_dup_fd; 558 if (stdin_fd > 0) { 559 stdin_dup_fd = stdin_fd; 560 } else { 561 stdin_dup_fd = os_dup(STDIN_FILENO); 562 #ifdef MSWIN 563 // Replace the original stdin with the console input handle. 564 os_replace_stdin_to_conin(); 565 #endif 566 } 567 return stdin_dup_fd; 568 } 569 570 /// Read from a file 571 /// 572 /// Handles EINTR, but not other errors. 573 /// 574 /// @param[in] fd File descriptor to read from. 575 /// @param[out] ret_eof Is set to true if EOF was encountered, otherwise set 576 /// to false. Initial value is ignored. 577 /// @param[out] ret_buf Buffer to write to. May be NULL if size is zero. 578 /// @param[in] size Amount of bytes to read. 579 /// @param[in] non_blocking Do not restart syscall if EAGAIN was encountered. 580 /// 581 /// @return Number of bytes read or libuv error code (< 0). 582 ptrdiff_t os_read(const int fd, bool *const ret_eof, char *const ret_buf, const size_t size, 583 const bool non_blocking) 584 FUNC_ATTR_WARN_UNUSED_RESULT 585 { 586 *ret_eof = false; 587 if (ret_buf == NULL) { 588 assert(size == 0); 589 return 0; 590 } 591 size_t read_bytes = 0; 592 while (read_bytes != size) { 593 assert(size >= read_bytes); 594 const ptrdiff_t cur_read_bytes = read(fd, ret_buf + read_bytes, 595 IO_COUNT(size - read_bytes)); 596 if (cur_read_bytes > 0) { 597 read_bytes += (size_t)cur_read_bytes; 598 } 599 if (cur_read_bytes < 0) { 600 const int error = os_translate_sys_error(errno); 601 errno = 0; 602 if (non_blocking && error == UV_EAGAIN) { 603 break; 604 } else if (error == UV_EINTR || error == UV_EAGAIN) { 605 continue; 606 } else { 607 return (ptrdiff_t)error; 608 } 609 } 610 if (cur_read_bytes == 0) { 611 *ret_eof = true; 612 break; 613 } 614 } 615 return (ptrdiff_t)read_bytes; 616 } 617 618 #ifdef HAVE_READV 619 /// Read from a file to multiple buffers at once 620 /// 621 /// Wrapper for readv(). 622 /// 623 /// @param[in] fd File descriptor to read from. 624 /// @param[out] ret_eof Is set to true if EOF was encountered, otherwise set 625 /// to false. Initial value is ignored. 626 /// @param[out] iov Description of buffers to write to. Note: this description 627 /// may change, it is incorrect to use data it points to after 628 /// os_readv(). 629 /// @param[in] iov_size Number of buffers in iov. 630 /// @param[in] non_blocking Do not restart syscall if EAGAIN was encountered. 631 /// 632 /// @return Number of bytes read or libuv error code (< 0). 633 ptrdiff_t os_readv(const int fd, bool *const ret_eof, struct iovec *iov, size_t iov_size, 634 const bool non_blocking) 635 FUNC_ATTR_NONNULL_ALL 636 { 637 *ret_eof = false; 638 size_t read_bytes = 0; 639 size_t toread = 0; 640 for (size_t i = 0; i < iov_size; i++) { 641 // Overflow, trying to read too much data 642 assert(toread <= SIZE_MAX - iov[i].iov_len); 643 toread += iov[i].iov_len; 644 } 645 while (read_bytes < toread && iov_size && !*ret_eof) { 646 ptrdiff_t cur_read_bytes = readv(fd, iov, (int)iov_size); 647 if (cur_read_bytes == 0) { 648 *ret_eof = true; 649 } 650 if (cur_read_bytes > 0) { 651 read_bytes += (size_t)cur_read_bytes; 652 while (iov_size && cur_read_bytes) { 653 if (cur_read_bytes < (ptrdiff_t)iov->iov_len) { 654 iov->iov_len -= (size_t)cur_read_bytes; 655 iov->iov_base = (char *)iov->iov_base + cur_read_bytes; 656 cur_read_bytes = 0; 657 } else { 658 cur_read_bytes -= (ptrdiff_t)iov->iov_len; 659 iov_size--; 660 iov++; 661 } 662 } 663 } else if (cur_read_bytes < 0) { 664 const int error = os_translate_sys_error(errno); 665 errno = 0; 666 if (non_blocking && error == UV_EAGAIN) { 667 break; 668 } else if (error == UV_EINTR || error == UV_EAGAIN) { 669 continue; 670 } else { 671 return (ptrdiff_t)error; 672 } 673 } 674 } 675 return (ptrdiff_t)read_bytes; 676 } 677 #endif // HAVE_READV 678 679 /// Write to a file 680 /// 681 /// @param[in] fd File descriptor to write to. 682 /// @param[in] buf Data to write. May be NULL if size is zero. 683 /// @param[in] size Amount of bytes to write. 684 /// @param[in] non_blocking Do not restart syscall if EAGAIN was encountered. 685 /// 686 /// @return Number of bytes written or libuv error code (< 0). 687 ptrdiff_t os_write(const int fd, const char *const buf, const size_t size, const bool non_blocking) 688 FUNC_ATTR_WARN_UNUSED_RESULT 689 { 690 if (buf == NULL) { 691 assert(size == 0); 692 return 0; 693 } 694 size_t written_bytes = 0; 695 while (written_bytes != size) { 696 assert(size >= written_bytes); 697 const ptrdiff_t cur_written_bytes = write(fd, buf + written_bytes, 698 IO_COUNT(size - written_bytes)); 699 if (cur_written_bytes > 0) { 700 written_bytes += (size_t)cur_written_bytes; 701 } 702 if (cur_written_bytes < 0) { 703 const int error = os_translate_sys_error(errno); 704 errno = 0; 705 if (non_blocking && error == UV_EAGAIN) { 706 break; 707 } else if (error == UV_EINTR || error == UV_EAGAIN) { 708 continue; 709 } else { 710 return error; 711 } 712 } 713 if (cur_written_bytes == 0) { 714 return UV_UNKNOWN; 715 } 716 } 717 return (ptrdiff_t)written_bytes; 718 } 719 720 /// Copies a file from `path` to `new_path`. 721 /// 722 /// @see http://docs.libuv.org/en/v1.x/fs.html#c.uv_fs_copyfile 723 /// 724 /// @param path Path of file to be copied 725 /// @param path_new Path of new file 726 /// @param flags Bitwise OR of flags defined in <uv.h> 727 /// @return 0 on success, or libuv error code on failure. 728 int os_copy(const char *path, const char *new_path, int flags) 729 { 730 int r; 731 RUN_UV_FS_FUNC(r, uv_fs_copyfile, path, new_path, flags, NULL); 732 return r; 733 } 734 735 /// Flushes file modifications to disk. 736 /// 737 /// @param fd the file descriptor of the file to flush to disk. 738 /// 739 /// @return 0 on success, or libuv error code on failure. 740 int os_fsync(int fd) 741 { 742 int r; 743 RUN_UV_FS_FUNC(r, uv_fs_fsync, fd, NULL); 744 g_stats.fsync++; 745 return r; 746 } 747 748 /// Get stat information for a file. 749 /// 750 /// @return libuv return code, or -errno 751 static int os_stat(const char *name, uv_stat_t *statbuf) 752 FUNC_ATTR_NONNULL_ARG(2) 753 { 754 if (!name) { 755 return UV_EINVAL; 756 } 757 uv_fs_t request; 758 int result = uv_fs_stat(NULL, &request, name, NULL); 759 if (result == kLibuvSuccess) { 760 *statbuf = request.statbuf; 761 } 762 uv_fs_req_cleanup(&request); 763 return result; 764 } 765 766 /// Get the file permissions for a given file. 767 /// 768 /// @return libuv error code on error. 769 int32_t os_getperm(const char *name) 770 { 771 uv_stat_t statbuf; 772 int stat_result = os_stat(name, &statbuf); 773 if (stat_result == kLibuvSuccess) { 774 return (int32_t)statbuf.st_mode; 775 } 776 return stat_result; 777 } 778 779 /// Set the permission of a file. 780 /// 781 /// @return `OK` for success, `FAIL` for failure. 782 int os_setperm(const char *const name, int perm) 783 FUNC_ATTR_NONNULL_ALL 784 { 785 int r; 786 RUN_UV_FS_FUNC(r, uv_fs_chmod, name, perm, NULL); 787 return (r == kLibuvSuccess ? OK : FAIL); 788 } 789 790 #ifdef HAVE_XATTR 791 /// Copy extended attributes from_file to to_file 792 void os_copy_xattr(const char *from_file, const char *to_file) 793 { 794 if (from_file == NULL) { 795 return; 796 } 797 798 // get the length of the extended attributes 799 ssize_t size = listxattr((char *)from_file, NULL, 0); 800 // not supported or no attributes to copy 801 if (size <= 0) { 802 return; 803 } 804 char *xattr_buf = xmalloc((size_t)size); 805 size = listxattr(from_file, xattr_buf, (size_t)size); 806 ssize_t tsize = size; 807 808 errno = 0; 809 810 ssize_t max_vallen = 0; 811 char *val = NULL; 812 const char *errmsg = NULL; 813 814 for (int round = 0; round < 2; round++) { 815 char *key = xattr_buf; 816 if (round == 1) { 817 size = tsize; 818 } 819 820 while (size > 0) { 821 ssize_t vallen = getxattr(from_file, key, val, round ? (size_t)max_vallen : 0); 822 // only set the attribute in the second round 823 if (vallen >= 0 && round 824 && setxattr(to_file, key, val, (size_t)vallen, 0) == 0) { 825 // 826 } else if (errno) { 827 switch (errno) { 828 case E2BIG: 829 errmsg = e_xattr_e2big; 830 goto error_exit; 831 case ENOTSUP: 832 case EACCES: 833 case EPERM: 834 break; 835 case ERANGE: 836 errmsg = e_xattr_erange; 837 goto error_exit; 838 default: 839 errmsg = e_xattr_other; 840 goto error_exit; 841 } 842 } 843 844 if (round == 0 && vallen > max_vallen) { 845 max_vallen = vallen; 846 } 847 848 // add one for terminating null 849 ssize_t keylen = (ssize_t)strlen(key) + 1; 850 size -= keylen; 851 key += keylen; 852 } 853 if (round) { 854 break; 855 } 856 857 val = xmalloc((size_t)max_vallen + 1); 858 } 859 error_exit: 860 xfree(xattr_buf); 861 xfree(val); 862 863 if (errmsg != NULL) { 864 emsg(_(errmsg)); 865 } 866 } 867 #endif 868 869 // Return a pointer to the ACL of file "fname" in allocated memory. 870 // Return NULL if the ACL is not available for whatever reason. 871 vim_acl_T os_get_acl(const char *fname) 872 { 873 vim_acl_T ret = NULL; 874 return ret; 875 } 876 877 // Set the ACL of file "fname" to "acl" (unless it's NULL). 878 void os_set_acl(const char *fname, vim_acl_T aclent) 879 { 880 if (aclent == NULL) { 881 return; 882 } 883 } 884 885 void os_free_acl(vim_acl_T aclent) 886 { 887 if (aclent == NULL) { 888 return; 889 } 890 } 891 892 #ifdef UNIX 893 /// Checks if the current user owns a file. 894 /// 895 /// Uses both uv_fs_stat() and uv_fs_lstat() via os_fileinfo() and 896 /// os_fileinfo_link() respectively for extra security. 897 bool os_file_owned(const char *fname) 898 FUNC_ATTR_NONNULL_ALL 899 { 900 uid_t uid = getuid(); 901 FileInfo finfo; 902 bool file_owned = os_fileinfo(fname, &finfo) && finfo.stat.st_uid == uid; 903 bool link_owned = os_fileinfo_link(fname, &finfo) && finfo.stat.st_uid == uid; 904 return file_owned && link_owned; 905 } 906 #else 907 bool os_file_owned(const char *fname) 908 { 909 return true; // TODO(justinmk): Windows. #8244 910 } 911 #endif 912 913 /// Changes the owner and group of a file, like chown(2). 914 /// 915 /// @return 0 on success, or libuv error code on failure. 916 /// 917 /// @note If `owner` or `group` is -1, then that ID is not changed. 918 int os_chown(const char *path, uv_uid_t owner, uv_gid_t group) 919 { 920 int r; 921 RUN_UV_FS_FUNC(r, uv_fs_chown, path, owner, group, NULL); 922 return r; 923 } 924 925 /// Changes the owner and group of the file referred to by the open file 926 /// descriptor, like fchown(2). 927 /// 928 /// @return 0 on success, or libuv error code on failure. 929 /// 930 /// @note If `owner` or `group` is -1, then that ID is not changed. 931 int os_fchown(int fd, uv_uid_t owner, uv_gid_t group) 932 { 933 int r; 934 RUN_UV_FS_FUNC(r, uv_fs_fchown, fd, owner, group, NULL); 935 return r; 936 } 937 938 /// Check if a path exists. 939 /// 940 /// @return `true` if `path` exists 941 bool os_path_exists(const char *path) 942 { 943 uv_stat_t statbuf; 944 return os_stat(path, &statbuf) == kLibuvSuccess; 945 } 946 947 /// Sets file access and modification times. 948 /// 949 /// @see POSIX utime(2) 950 /// 951 /// @param path File path. 952 /// @param atime Last access time. 953 /// @param mtime Last modification time. 954 /// 955 /// @return 0 on success, or negative error code. 956 int os_file_settime(const char *path, double atime, double mtime) 957 { 958 int r; 959 RUN_UV_FS_FUNC(r, uv_fs_utime, path, atime, mtime, NULL); 960 return r; 961 } 962 963 /// Check if a file is readable. 964 /// 965 /// @return true if `name` is readable, otherwise false. 966 bool os_file_is_readable(const char *name) 967 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT 968 { 969 int r; 970 RUN_UV_FS_FUNC(r, uv_fs_access, name, R_OK, NULL); 971 return (r == 0); 972 } 973 974 /// Check if a file is writable. 975 /// 976 /// @return `0` if `name` is not writable, 977 /// @return `1` if `name` is writable, 978 /// @return `2` for a directory which we have rights to write into. 979 int os_file_is_writable(const char *name) 980 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT 981 { 982 int r; 983 RUN_UV_FS_FUNC(r, uv_fs_access, name, W_OK, NULL); 984 if (r == 0) { 985 return os_isdir(name) ? 2 : 1; 986 } 987 return 0; 988 } 989 990 /// Rename a file or directory. 991 /// 992 /// @return `OK` for success, `FAIL` for failure. 993 int os_rename(const char *path, const char *new_path) 994 FUNC_ATTR_NONNULL_ALL 995 { 996 int r; 997 RUN_UV_FS_FUNC(r, uv_fs_rename, path, new_path, NULL); 998 return (r == kLibuvSuccess ? OK : FAIL); 999 } 1000 1001 /// Make a directory. 1002 /// 1003 /// @return `0` for success, libuv error code for failure. 1004 int os_mkdir(const char *path, int32_t mode) 1005 FUNC_ATTR_NONNULL_ALL 1006 { 1007 int r; 1008 RUN_UV_FS_FUNC(r, uv_fs_mkdir, path, mode, NULL); 1009 return r; 1010 } 1011 1012 /// Make a directory, with higher levels when needed 1013 /// 1014 /// @param[in] dir Directory to create. 1015 /// @param[in] mode Permissions for the newly-created directory. 1016 /// @param[out] failed_dir If it failed to create directory, then this 1017 /// argument is set to an allocated string containing 1018 /// the name of the directory which os_mkdir_recurse 1019 /// failed to create. I.e. it will contain dir or any 1020 /// of the higher level directories. 1021 /// @param[out] created Set to the full name of the first created directory. 1022 /// It will be NULL until that happens. 1023 /// 1024 /// @return `0` for success, libuv error code for failure. 1025 int os_mkdir_recurse(const char *const dir, int32_t mode, char **const failed_dir, 1026 char **const created) 1027 FUNC_ATTR_NONNULL_ARG(1, 3) FUNC_ATTR_WARN_UNUSED_RESULT 1028 { 1029 // Get end of directory name in "dir". 1030 // We're done when it's "/" or "c:/". 1031 const size_t dirlen = strlen(dir); 1032 char *const curdir = xmemdupz(dir, dirlen); 1033 char *const past_head = get_past_head(curdir); 1034 char *e = curdir + dirlen; 1035 const char *const real_end = e; 1036 const char past_head_save = *past_head; 1037 while (!os_isdir(curdir)) { 1038 e = path_tail_with_sep(curdir); 1039 if (e <= past_head) { 1040 *past_head = NUL; 1041 break; 1042 } 1043 *e = NUL; 1044 } 1045 while (e != real_end) { 1046 if (e > past_head) { 1047 *e = PATHSEP; 1048 } else { 1049 *past_head = past_head_save; 1050 } 1051 const size_t component_len = strlen(e); 1052 e += component_len; 1053 if (e == real_end 1054 && memcnt(e - component_len, PATHSEP, component_len) == component_len) { 1055 // Path ends with something like "////". Ignore this. 1056 break; 1057 } 1058 int ret; 1059 if ((ret = os_mkdir(curdir, mode)) != 0) { 1060 *failed_dir = curdir; 1061 return ret; 1062 } else if (created != NULL && *created == NULL) { 1063 *created = FullName_save(curdir, false); 1064 } 1065 } 1066 xfree(curdir); 1067 return 0; 1068 } 1069 1070 /// Create the parent directory of a file if it does not exist 1071 /// 1072 /// @param[in] fname Full path of the file name whose parent directories 1073 /// we want to create 1074 /// @param[in] mode Permissions for the newly-created directory. 1075 /// 1076 /// @return `0` for success, libuv error code for failure. 1077 int os_file_mkdir(char *fname, int32_t mode) 1078 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT 1079 { 1080 if (!dir_of_file_exists(fname)) { 1081 char *tail = path_tail_with_sep(fname); 1082 char *last_char = tail + strlen(tail) - 1; 1083 if (vim_ispathsep(*last_char)) { 1084 emsg(_(e_noname)); 1085 return -1; 1086 } 1087 char c = *tail; 1088 *tail = NUL; 1089 int r; 1090 char *failed_dir; 1091 if (((r = os_mkdir_recurse(fname, mode, &failed_dir, NULL)) < 0)) { 1092 semsg(_(e_mkdir), failed_dir, os_strerror(r)); 1093 xfree(failed_dir); 1094 } 1095 *tail = c; 1096 return r; 1097 } 1098 return 0; 1099 } 1100 1101 /// Create a unique temporary directory. 1102 /// 1103 /// @param[in] templ Template of the path to the directory with XXXXXX 1104 /// which would be replaced by random chars. 1105 /// @param[out] path Path to created directory for success, undefined for 1106 /// failure. 1107 /// @return `0` for success, non-zero for failure. 1108 int os_mkdtemp(const char *templ, char *path) 1109 FUNC_ATTR_NONNULL_ALL 1110 { 1111 uv_fs_t request; 1112 int result = uv_fs_mkdtemp(NULL, &request, templ, NULL); 1113 if (result == kLibuvSuccess) { 1114 xstrlcpy(path, request.path, TEMP_FILE_PATH_MAXLEN); 1115 } 1116 uv_fs_req_cleanup(&request); 1117 return result; 1118 } 1119 1120 /// Remove a directory. 1121 /// 1122 /// @return `0` for success, non-zero for failure. 1123 int os_rmdir(const char *path) 1124 FUNC_ATTR_NONNULL_ALL 1125 { 1126 int r; 1127 RUN_UV_FS_FUNC(r, uv_fs_rmdir, path, NULL); 1128 return r; 1129 } 1130 1131 /// Opens a directory. 1132 /// @param[out] dir The Directory object. 1133 /// @param path Path to the directory. 1134 /// @returns true if dir contains one or more items, false if not or an error 1135 /// occurred. 1136 bool os_scandir(Directory *dir, const char *path) 1137 FUNC_ATTR_NONNULL_ALL 1138 { 1139 int r = uv_fs_scandir(NULL, &dir->request, path, 0, NULL); 1140 if (r < 0) { 1141 os_closedir(dir); 1142 } 1143 return r >= 0; 1144 } 1145 1146 /// Increments the directory pointer. 1147 /// @param dir The Directory object. 1148 /// @returns a pointer to the next path in `dir` or `NULL`. 1149 const char *os_scandir_next(Directory *dir) 1150 FUNC_ATTR_NONNULL_ALL 1151 { 1152 int err = uv_fs_scandir_next(&dir->request, &dir->ent); 1153 return err != UV_EOF ? dir->ent.name : NULL; 1154 } 1155 1156 /// Frees memory associated with `os_scandir()`. 1157 /// @param dir The directory. 1158 void os_closedir(Directory *dir) 1159 FUNC_ATTR_NONNULL_ALL 1160 { 1161 uv_fs_req_cleanup(&dir->request); 1162 } 1163 1164 /// Remove a file. 1165 /// 1166 /// @return `0` for success, non-zero for failure. 1167 int os_remove(const char *path) 1168 FUNC_ATTR_NONNULL_ALL 1169 { 1170 int r; 1171 RUN_UV_FS_FUNC(r, uv_fs_unlink, path, NULL); 1172 return r; 1173 } 1174 1175 /// Get the file information for a given path 1176 /// 1177 /// @param path Path to the file. 1178 /// @param[out] file_info Pointer to a FileInfo to put the information in. 1179 /// @return `true` on success, `false` for failure. 1180 bool os_fileinfo(const char *path, FileInfo *file_info) 1181 FUNC_ATTR_NONNULL_ARG(2) 1182 { 1183 CLEAR_POINTER(file_info); 1184 return os_stat(path, &(file_info->stat)) == kLibuvSuccess; 1185 } 1186 1187 /// Get the file information for a given path without following links 1188 /// 1189 /// @param path Path to the file. 1190 /// @param[out] file_info Pointer to a FileInfo to put the information in. 1191 /// @return `true` on success, `false` for failure. 1192 bool os_fileinfo_link(const char *path, FileInfo *file_info) 1193 FUNC_ATTR_NONNULL_ARG(2) 1194 { 1195 CLEAR_POINTER(file_info); 1196 if (path == NULL) { 1197 return false; 1198 } 1199 uv_fs_t request; 1200 bool ok = uv_fs_lstat(NULL, &request, path, NULL) == kLibuvSuccess; 1201 if (ok) { 1202 file_info->stat = request.statbuf; 1203 } 1204 uv_fs_req_cleanup(&request); 1205 return ok; 1206 } 1207 1208 /// Get the file information for a given file descriptor 1209 /// 1210 /// @param file_descriptor File descriptor of the file. 1211 /// @param[out] file_info Pointer to a FileInfo to put the information in. 1212 /// @return `true` on success, `false` for failure. 1213 bool os_fileinfo_fd(int file_descriptor, FileInfo *file_info) 1214 FUNC_ATTR_NONNULL_ALL 1215 { 1216 uv_fs_t request; 1217 CLEAR_POINTER(file_info); 1218 bool ok = uv_fs_fstat(NULL, 1219 &request, 1220 file_descriptor, 1221 NULL) == kLibuvSuccess; 1222 if (ok) { 1223 file_info->stat = request.statbuf; 1224 } 1225 uv_fs_req_cleanup(&request); 1226 return ok; 1227 } 1228 1229 /// Compare the inodes of two FileInfos 1230 /// 1231 /// @return `true` if the two FileInfos represent the same file. 1232 bool os_fileinfo_id_equal(const FileInfo *file_info_1, const FileInfo *file_info_2) 1233 FUNC_ATTR_NONNULL_ALL 1234 { 1235 return file_info_1->stat.st_ino == file_info_2->stat.st_ino 1236 && file_info_1->stat.st_dev == file_info_2->stat.st_dev; 1237 } 1238 1239 /// Get the `FileID` of a `FileInfo` 1240 /// 1241 /// @param file_info Pointer to the `FileInfo` 1242 /// @param[out] file_id Pointer to a `FileID` 1243 void os_fileinfo_id(const FileInfo *file_info, FileID *file_id) 1244 FUNC_ATTR_NONNULL_ALL 1245 { 1246 file_id->inode = file_info->stat.st_ino; 1247 file_id->device_id = file_info->stat.st_dev; 1248 } 1249 1250 /// Get the inode of a `FileInfo` 1251 /// 1252 /// @deprecated Use `FileID` instead, this function is only needed in memline.c 1253 /// @param file_info Pointer to the `FileInfo` 1254 /// @return the inode number 1255 uint64_t os_fileinfo_inode(const FileInfo *file_info) 1256 FUNC_ATTR_NONNULL_ALL 1257 { 1258 return file_info->stat.st_ino; 1259 } 1260 1261 /// Get the size of a file from a `FileInfo`. 1262 /// 1263 /// @return filesize in bytes. 1264 uint64_t os_fileinfo_size(const FileInfo *file_info) 1265 FUNC_ATTR_NONNULL_ALL 1266 { 1267 return file_info->stat.st_size; 1268 } 1269 1270 /// Get the number of hardlinks from a `FileInfo`. 1271 /// 1272 /// @return number of hardlinks. 1273 uint64_t os_fileinfo_hardlinks(const FileInfo *file_info) 1274 FUNC_ATTR_NONNULL_ALL 1275 { 1276 return file_info->stat.st_nlink; 1277 } 1278 1279 /// Get the blocksize from a `FileInfo`. 1280 /// 1281 /// @return blocksize in bytes. 1282 uint64_t os_fileinfo_blocksize(const FileInfo *file_info) 1283 FUNC_ATTR_NONNULL_ALL 1284 { 1285 return file_info->stat.st_blksize; 1286 } 1287 1288 /// Get the `FileID` for a given path 1289 /// 1290 /// @param path Path to the file. 1291 /// @param[out] file_info Pointer to a `FileID` to fill in. 1292 /// @return `true` on success, `false` for failure. 1293 bool os_fileid(const char *path, FileID *file_id) 1294 FUNC_ATTR_NONNULL_ALL 1295 { 1296 uv_stat_t statbuf; 1297 if (os_stat(path, &statbuf) == kLibuvSuccess) { 1298 file_id->inode = statbuf.st_ino; 1299 file_id->device_id = statbuf.st_dev; 1300 return true; 1301 } 1302 return false; 1303 } 1304 1305 /// Check if two `FileID`s are equal 1306 /// 1307 /// @param file_id_1 Pointer to first `FileID` 1308 /// @param file_id_2 Pointer to second `FileID` 1309 /// @return `true` if the two `FileID`s represent te same file. 1310 bool os_fileid_equal(const FileID *file_id_1, const FileID *file_id_2) 1311 FUNC_ATTR_NONNULL_ALL 1312 { 1313 return file_id_1->inode == file_id_2->inode 1314 && file_id_1->device_id == file_id_2->device_id; 1315 } 1316 1317 /// Check if a `FileID` is equal to a `FileInfo` 1318 /// 1319 /// @param file_id Pointer to a `FileID` 1320 /// @param file_info Pointer to a `FileInfo` 1321 /// @return `true` if the `FileID` and the `FileInfo` represent te same file. 1322 bool os_fileid_equal_fileinfo(const FileID *file_id, const FileInfo *file_info) 1323 FUNC_ATTR_NONNULL_ALL 1324 { 1325 return file_id->inode == file_info->stat.st_ino 1326 && file_id->device_id == file_info->stat.st_dev; 1327 } 1328 1329 /// Return the canonicalized absolute pathname. 1330 /// 1331 /// @param[in] name Filename to be canonicalized. 1332 /// @param[out] buf Buffer to store the canonicalized values. 1333 /// If it is NULL, memory is allocated. In that case, the caller 1334 /// should deallocate this buffer. 1335 /// @param[in] len The length of the buffer. 1336 /// 1337 /// @return pointer to the buf on success, or NULL. 1338 char *os_realpath(const char *name, char *buf, size_t len) 1339 FUNC_ATTR_NONNULL_ARG(1) 1340 { 1341 uv_fs_t request; 1342 int result = uv_fs_realpath(NULL, &request, name, NULL); 1343 if (result == kLibuvSuccess) { 1344 if (buf == NULL) { 1345 buf = xmalloc(len); 1346 } 1347 xstrlcpy(buf, request.ptr, len); 1348 } 1349 uv_fs_req_cleanup(&request); 1350 return result == kLibuvSuccess ? buf : NULL; 1351 } 1352 1353 #ifdef MSWIN 1354 /// When "fname" is the name of a shortcut (*.lnk) resolve the file it points 1355 /// to and return that name in allocated memory. 1356 /// Otherwise NULL is returned. 1357 char *os_resolve_shortcut(const char *fname) 1358 FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC 1359 { 1360 HRESULT hr; 1361 IPersistFile *ppf = NULL; 1362 OLECHAR wsz[MAX_PATH]; 1363 char *rfname = NULL; 1364 IShellLinkW *pslw = NULL; 1365 WIN32_FIND_DATAW ffdw; 1366 1367 // Check if the file name ends in ".lnk". Avoid calling CoCreateInstance(), 1368 // it's quite slow. 1369 if (fname == NULL) { 1370 return rfname; 1371 } 1372 const size_t len = strlen(fname); 1373 if (len <= 4 || STRNICMP(fname + len - 4, ".lnk", 4) != 0) { 1374 return rfname; 1375 } 1376 1377 CoInitialize(NULL); 1378 1379 // create a link manager object and request its interface 1380 hr = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, 1381 &IID_IShellLinkW, (void **)&pslw); 1382 if (hr == S_OK) { 1383 wchar_t *p; 1384 const int r = utf8_to_utf16(fname, -1, &p); 1385 if (r != 0) { 1386 semsg("utf8_to_utf16 failed: %d", r); 1387 } else if (p != NULL) { 1388 // Get a pointer to the IPersistFile interface. 1389 hr = pslw->lpVtbl->QueryInterface(pslw, &IID_IPersistFile, (void **)&ppf); 1390 if (hr != S_OK) { 1391 goto shortcut_errorw; 1392 } 1393 1394 // "load" the name and resolve the link 1395 hr = ppf->lpVtbl->Load(ppf, p, STGM_READ); 1396 if (hr != S_OK) { 1397 goto shortcut_errorw; 1398 } 1399 1400 # if 0 // This makes Vim wait a long time if the target does not exist. 1401 hr = pslw->lpVtbl->Resolve(pslw, NULL, SLR_NO_UI); 1402 if (hr != S_OK) { 1403 goto shortcut_errorw; 1404 } 1405 # endif 1406 1407 // Get the path to the link target. 1408 ZeroMemory(wsz, MAX_PATH * sizeof(wchar_t)); 1409 hr = pslw->lpVtbl->GetPath(pslw, wsz, MAX_PATH, &ffdw, 0); 1410 if (hr == S_OK && wsz[0] != NUL) { 1411 const int r2 = utf16_to_utf8(wsz, -1, &rfname); 1412 if (r2 != 0) { 1413 semsg("utf16_to_utf8 failed: %d", r2); 1414 } 1415 } 1416 1417 shortcut_errorw: 1418 xfree(p); 1419 goto shortcut_end; 1420 } 1421 } 1422 1423 shortcut_end: 1424 // Release all interface pointers (both belong to the same object) 1425 if (ppf != NULL) { 1426 ppf->lpVtbl->Release(ppf); 1427 } 1428 if (pslw != NULL) { 1429 pslw->lpVtbl->Release(pslw); 1430 } 1431 1432 CoUninitialize(); 1433 return rfname; 1434 } 1435 1436 # define IS_PATH_SEP(c) ((c) == L'\\' || (c) == L'/') 1437 /// Returns true if the path contains a reparse point (junction or symbolic 1438 /// link). Otherwise false in returned. 1439 bool os_is_reparse_point_include(const char *path) 1440 { 1441 wchar_t *p, *q, *utf16_path; 1442 wchar_t buf[MAX_PATH]; 1443 DWORD attr; 1444 bool result = false; 1445 1446 const int r = utf8_to_utf16(path, -1, &utf16_path); 1447 if (r != 0) { 1448 semsg("utf8_to_utf16 failed: %d", r); 1449 return false; 1450 } 1451 1452 p = utf16_path; 1453 if (isalpha((uint8_t)p[0]) && p[1] == L':' && IS_PATH_SEP(p[2])) { 1454 p += 3; 1455 } else if (IS_PATH_SEP(p[0]) && IS_PATH_SEP(p[1])) { 1456 p += 2; 1457 } 1458 1459 while (*p != L'\0') { 1460 q = wcspbrk(p, L"\\/"); 1461 if (q == NULL) { 1462 p = q = utf16_path + wcslen(utf16_path); 1463 } else { 1464 p = q + 1; 1465 } 1466 if (q - utf16_path >= MAX_PATH) { 1467 break; 1468 } 1469 wcsncpy(buf, utf16_path, (size_t)(q - utf16_path)); 1470 buf[q - utf16_path] = L'\0'; 1471 attr = GetFileAttributesW(buf); 1472 if (attr != INVALID_FILE_ATTRIBUTES 1473 && (attr & FILE_ATTRIBUTE_REPARSE_POINT) != 0) { 1474 result = true; 1475 break; 1476 } 1477 } 1478 xfree(utf16_path); 1479 return result; 1480 } 1481 #endif