📄 path.cc
字号:
/* path.cc: path support. Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.This file is part of Cygwin.This software is a copyrighted work licensed under the terms of theCygwin license. Please consult the file "CYGWIN_LICENSE" fordetails. *//* This module's job is to - convert between POSIX and Win32 style filenames, - support the `mount' functionality, - support symlinks for files and directories Pathnames are handled as follows: - A \ or : in a path denotes a pure windows spec. - Paths beginning with // (or \\) are not translated (i.e. looked up in the mount table) and are assumed to be UNC path names. The goal in the above set of rules is to allow both POSIX and Win32 flavors of pathnames without either interfering. The rules are intended to be as close to a superset of both as possible. Note that you can have more than one path to a file. The mount table is always prefered when translating Win32 paths to POSIX paths. Win32 paths in mount table entries may be UNC paths or standard Win32 paths starting with <drive-letter>: Text vs Binary issues are not considered here in path style decisions, although the appropriate flags are retrieved and stored in various structures. Removing mounted filesystem support would simplify things greatly, but having it gives us a mechanism of treating disk that lives on a UNIX machine as having UNIX semantics [it allows one to edit a text file on that disk and not have cr's magically appear and perhaps break apps running on UNIX boxes]. It also useful to be able to layout a hierarchy without changing the underlying directories. The semantics of mounting file systems is not intended to precisely follow normal UNIX systems. Each DOS drive is defined to have a current directory. Supporting this would complicate things so for now things are defined so that c: means c:\. FIXME: Is this still true?*/#include "winsup.h"#include <stdio.h>#include <stdlib.h>#include <sys/mount.h>#include <mntent.h>#include <unistd.h>#include <errno.h>#include <ctype.h>#include <winioctl.h>#include <wingdi.h>#include <winuser.h>#include <winnls.h>#include <winnetwk.h>#include <sys/cygwin.h>#include <cygwin/version.h>#include "cygerrno.h"#include "security.h"#include "fhandler.h"#include "path.h"#include "sync.h"#include "sigproc.h"#include "pinfo.h"#include "dtable.h"#include "cygheap.h"#include "shared_info.h"#include "registry.h"#include <assert.h>#ifdef _MT_SAFE#define iteration _reent_winsup ()->_iteration#define available_drives _reent_winsup ()->available_drives#elsestatic int iteration;static DWORD available_drives;#endifstatic int normalize_win32_path (const char *src, char *dst);static void slashify (const char *src, char *dst, int trailing_slash_p);static void backslashify (const char *src, char *dst, int trailing_slash_p);struct symlink_info{ char contents[MAX_PATH + 4]; char *ext_here; int extn; unsigned pflags; DWORD fileattr; int is_symlink; bool ext_tacked_on; int error; bool case_clash; int check (char *path, const suffix_info *suffixes, unsigned opt); BOOL case_check (char *path);};int pcheck_case = PCHECK_RELAXED; /* Determines the case check behaviour. */static char shortcut_header[SHORTCUT_HDR_SIZE];static BOOL shortcut_initalized;static voidcreate_shortcut_header (void){ if (!shortcut_initalized) { shortcut_header[0] = 'L'; shortcut_header[4] = '\001'; shortcut_header[5] = '\024'; shortcut_header[6] = '\002'; shortcut_header[12] = '\300'; shortcut_header[19] = 'F'; shortcut_header[20] = '\f'; shortcut_header[60] = '\001'; shortcut_initalized = TRUE; }}#define CYGWIN_REGNAME (cygheap->cygwin_regname ?: CYGWIN_INFO_CYGWIN_REGISTRY_NAME)/* Determine if path prefix matches current cygdrive */#define iscygdrive(path) \ (path_prefix_p (mount_table->cygdrive, (path), mount_table->cygdrive_len))#define iscygdrive_device(path) \ (isalpha (path[mount_table->cygdrive_len]) && \ (isdirsep (path[mount_table->cygdrive_len + 1]) || \ !path[mount_table->cygdrive_len + 1]))#define isproc(path) \ (path_prefix_p (proc, (path), proc_len))#define isvirtual_dev(devn) \ (devn == FH_CYGDRIVE || devn == FH_PROC || devn == FH_REGISTRY || devn == FH_PROCESS)/* Return non-zero if PATH1 is a prefix of PATH2. Both are assumed to be of the same path style and / vs \ usage. Neither may be "". LEN1 = strlen (PATH1). It's passed because often it's already known. Examples: /foo/ is a prefix of /foo <-- may seem odd, but desired /foo is a prefix of /foo/ / is a prefix of /foo/bar / is not a prefix of foo/bar foo/ is a prefix foo/bar /foo is not a prefix of /foobar*/intpath_prefix_p (const char *path1, const char *path2, int len1){ /* Handle case where PATH1 has trailing '/' and when it doesn't. */ if (len1 > 0 && SLASH_P (path1[len1 - 1])) len1--; if (len1 == 0) return SLASH_P (path2[0]) && !SLASH_P (path2[1]); if (!pathnmatch (path1, path2, len1)) return 0; return SLASH_P (path2[len1]) || path2[len1] == 0 || path1[len1 - 1] == ':';}/* Return non-zero if paths match in first len chars. Check is dependent of the case sensitivity setting. */intpathnmatch (const char *path1, const char *path2, int len){ return pcheck_case == PCHECK_STRICT ? !strncmp (path1, path2, len) : strncasematch (path1, path2, len);}/* Return non-zero if paths match. Check is dependent of the case sensitivity setting. */intpathmatch (const char *path1, const char *path2){ return pcheck_case == PCHECK_STRICT ? !strcmp (path1, path2) : strcasematch (path1, path2);}/* Normalize a POSIX path. \'s are converted to /'s in the process. All duplicate /'s, except for 2 leading /'s, are deleted. The result is 0 for success, or an errno error value. */#define isslash(c) ((c) == '/')static intnormalize_posix_path (const char *src, char *dst){ const char *src_start = src; char *dst_start = dst; syscall_printf ("src %s", src); if (isdrive (src) || strpbrk (src, "\\:")) { cygwin_conv_to_full_posix_path (src, dst); return 0; } if (!isslash (src[0])) { if (!cygheap->cwd.get (dst)) return get_errno (); dst = strchr (dst, '\0'); if (*src == '.') { if (dst == dst_start + 1 && *dst_start == '/') --dst; goto sawdot; } if (dst > dst_start && !isslash (dst[-1])) *dst++ = '/'; } /* Two leading /'s? If so, preserve them. */ else if (isslash (src[1])) { *dst++ = '/'; *dst++ = '/'; src += 2; if (isslash (*src)) { /* Starts with three or more slashes - reset. */ dst = dst_start; *dst++ = '/'; src = src_start + 1; } else if (src[0] == '.' && isslash (src[1])) { *dst++ = '.'; *dst++ = '/'; src += 2; } } else *dst = '\0'; while (*src) { /* Strip runs of /'s. */ if (!isslash (*src)) *dst++ = *src++; else { while (*++src) { if (isslash (*src)) continue; if (*src != '.') break; sawdot: if (src[1] != '.') { if (!src[1]) { if (dst == dst_start) *dst++ = '/'; goto done; } if (!isslash (src[1])) break; } else if (src[2] && !isslash (src[2])) { if (src[2] == '.') return ENOENT; break; } else { while (dst > dst_start && !isslash (*--dst)) continue; src++; } } *dst++ = '/'; } if ((dst - dst_start) >= MAX_PATH) { debug_printf ("ENAMETOOLONG = normalize_posix_path (%s)", src); return ENAMETOOLONG; } }done: *dst = '\0'; if (--dst > dst_start && isslash (*dst)) *dst = '\0'; debug_printf ("%s = normalize_posix_path (%s)", dst_start, src_start); return 0;}inline voidpath_conv::add_ext_from_sym (symlink_info &sym){ if (sym.ext_here && *sym.ext_here) { known_suffix = path + sym.extn; if (sym.ext_tacked_on) strcpy (known_suffix, sym.ext_here); }}static void __stdcall mkrelpath (char *dst) __attribute__ ((regparm (2)));static void __stdcallmkrelpath (char *path){ char cwd_win32[MAX_PATH]; if (!cygheap->cwd.get (cwd_win32, 0)) return; unsigned cwdlen = strlen (cwd_win32); if (!path_prefix_p (cwd_win32, path, cwdlen)) return; size_t n = strlen (path); if (n < cwdlen) return; char *tail = path; if (n == cwdlen) tail += cwdlen; else tail += isdirsep (cwd_win32[cwdlen - 1]) ? cwdlen : cwdlen + 1; memmove (path, tail, strlen (tail) + 1); if (!*path) strcpy (path, ".");}boolfs_info::update (const char *win32_path){ char tmp_buf [MAX_PATH]; strncpy (tmp_buf, win32_path, MAX_PATH); if (!rootdir (tmp_buf)) { debug_printf ("Cannot get root component of path %s", win32_path); name [0] = '\0'; sym_opt = flags = serial = 0; return false; } if (strcmp (tmp_buf, root_dir) == 0) return 1; strncpy (root_dir, tmp_buf, MAX_PATH); drive_type = GetDriveType (root_dir); if (drive_type == DRIVE_REMOTE || (drive_type == DRIVE_UNKNOWN && (root_dir[0] == '\\' && root_dir[1] == '\\'))) is_remote_drive = 1; else is_remote_drive = 0; if (!GetVolumeInformation (root_dir, NULL, 0, &serial, NULL, &flags, name, sizeof (name))) { debug_printf ("Cannot get volume information (%s), %E", root_dir); name [0] = '\0'; sym_opt = flags = serial = 0; return false; } /* FIXME: Samba by default returns "NTFS" in file system name, but * doesn't support Extended Attributes. If there's some fast way to * distinguish between samba and real ntfs, it should be implemented * here. */ sym_opt = (!is_remote_drive && strcmp (name, "NTFS") == 0) ? PC_CHECK_EA : 0; return true;}char *path_conv::return_and_clear_normalized_path (){ char *s = normalized_path; normalized_path = NULL; return s;}voidpath_conv::fillin (HANDLE h){ BY_HANDLE_FILE_INFORMATION local; if (!GetFileInformationByHandle (h, &local)) { fileattr = INVALID_FILE_ATTRIBUTES; fs.serial = 0; } else { fileattr = local.dwFileAttributes; fs.serial = local.dwVolumeSerialNumber; } fs.drive_type = DRIVE_UNKNOWN;}/* Convert an arbitrary path SRC to a pure Win32 path, suitable for passing to Win32 API routines. If an error occurs, `error' is set to the errno value. Otherwise it is set to 0. follow_mode values: SYMLINK_FOLLOW - convert to PATH symlink points to SYMLINK_NOFOLLOW - convert to PATH of symlink itself SYMLINK_IGNORE - do not check PATH for symlinks SYMLINK_CONTENTS - just return symlink contents*/voidpath_conv::check (const char *src, unsigned opt, const suffix_info *suffixes){ /* This array is used when expanding symlinks. It is MAX_PATH * 2 in length so that we can hold the expanded symlink plus a trailer. */ char path_copy[MAX_PATH + 3]; char tmp_buf[2 * MAX_PATH + 3]; symlink_info sym; bool need_directory = 0; bool saw_symlinks = 0; int is_relpath; char *tail; sigframe thisframe (mainthread);#if 0 static path_conv last_path_conv; static char last_src[MAX_PATH + 1]; if (*last_src && strcmp (last_src, src) == 0) { *this = last_path_conv; return; }#endif int loop = 0; path_flags = 0; known_suffix = NULL; fileattr = INVALID_FILE_ATTRIBUTES; case_clash = false; devn = unit = 0; fs.root_dir[0] = '\0'; fs.name[0] = '\0'; fs.flags = fs.serial = 0; fs.sym_opt = 0; fs.drive_type = 0; fs.is_remote_drive = 0; normalized_path = NULL; if (!(opt & PC_NULLEMPTY)) error = 0; else if ((error = check_null_empty_str (src))) return; /* This loop handles symlink expansion. */ for (;;) { MALLOC_CHECK; assert (src); char *p = strrchr (src, '\0'); /* Detect if the user was looking for a directory. We have to strip the trailing slash initially and add it back on at the end due to Windows brain damage. */ if (--p > src) { if (isdirsep (*p)) need_directory = 1; else if (--p > src && p[1] == '.' && isdirsep (*p)) need_directory = 1; } is_relpath = !isabspath (src); error = normalize_posix_path (src, path_copy); if (error) return; tail = strchr (path_copy, '\0'); // Point to end of copy char *path_end = tail; tail[1] = '\0';
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -