# HG changeset patch # User Bram Moolenaar # Date 1371069697 -7200 # Node ID fa98c2b030ed5ac8d8c8139405d20ad6c611c065 # Parent 74a330469e22603da6c48948e7245bd065c67609 updated for version 7.3.1182 Problem: 'backupcopy' default on MS-Windows does not work for hard and soft links. Solution: Check for links. (David Pope, Ken Takata) diff --git a/src/fileio.c b/src/fileio.c --- a/src/fileio.c +++ b/src/fileio.c @@ -3780,12 +3780,12 @@ buf_write(buf, fname, sfname, start, end } } -# ifdef UNIX /* * Break symlinks and/or hardlinks if we've been asked to. */ if ((bkc_flags & BKC_BREAKSYMLINK) || (bkc_flags & BKC_BREAKHARDLINK)) { +# ifdef UNIX int lstat_res; lstat_res = mch_lstat((char *)fname, &st); @@ -3801,8 +3801,18 @@ buf_write(buf, fname, sfname, start, end && st_old.st_nlink > 1 && (lstat_res != 0 || st.st_ino == st_old.st_ino)) backup_copy = FALSE; - } -#endif +# else +# if defined(WIN32) + /* Symlinks. */ + if ((bkc_flags & BKC_BREAKSYMLINK) && mch_is_symbolic_link(fname)) + backup_copy = FALSE; + + /* Hardlinks. */ + if ((bkc_flags & BKC_BREAKHARDLINK) && mch_is_hard_link(fname)) + backup_copy = FALSE; +# endif +# endif + } #endif diff --git a/src/os_win32.c b/src/os_win32.c --- a/src/os_win32.c +++ b/src/os_win32.c @@ -78,6 +78,16 @@ # endif #endif +/* + * Reparse Point + */ +#ifndef FILE_ATTRIBUTE_REPARSE_POINT +# define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400 +#endif +#ifndef IO_REPARSE_TAG_SYMLINK +# define IO_REPARSE_TAG_SYMLINK 0xA000000C +#endif + /* Record all output and all keyboard & mouse input */ /* #define MCH_WRITE_DUMP */ @@ -219,6 +229,10 @@ static int need_vimrun_warning = FALSE; static char *vimrun_path = "vimrun "; #endif +static int win32_getattrs(char_u *name); +static int win32_setattrs(char_u *name, int attrs); +static int win32_set_archive(char_u *name); + #ifndef FEAT_GUI_W32 static int suppress_winsize = 1; /* don't fiddle with console */ #endif @@ -2623,57 +2637,54 @@ mch_dirname( /* * get file permissions for `name' * -1 : error - * else FILE_ATTRIBUTE_* defined in winnt.h + * else mode_t */ long mch_getperm(char_u *name) { -#ifdef FEAT_MBYTE - if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) - { - WCHAR *p = enc_to_utf16(name, NULL); - long n; - - if (p != NULL) - { - n = (long)GetFileAttributesW(p); - vim_free(p); - if (n >= 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) - return n; - /* Retry with non-wide function (for Windows 98). */ - } - } -#endif - return (long)GetFileAttributes((char *)name); + struct stat st; + int n; + + n = mch_stat(name, &st); + return n == 0 ? (int)st.st_mode : -1; } /* * set file permission for `name' to `perm' + * + * return FAIL for failure, OK otherwise */ int mch_setperm( char_u *name, long perm) { - perm |= FILE_ATTRIBUTE_ARCHIVE; /* file has changed, set archive bit */ + long n; #ifdef FEAT_MBYTE + WCHAR *p; if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) { - WCHAR *p = enc_to_utf16(name, NULL); - long n; + p = enc_to_utf16(name, NULL); if (p != NULL) { - n = (long)SetFileAttributesW(p, perm); + n = _wchmod(p, perm); vim_free(p); - if (n || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) - return n ? OK : FAIL; + if (n == -1 && GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) + return FAIL; /* Retry with non-wide function (for Windows 98). */ } } + if (p == NULL) #endif - return SetFileAttributes((char *)name, perm) ? OK : FAIL; + n = _chmod(name, perm); + if (n == -1) + return FAIL; + + win32_set_archive(name); + + return OK; } /* @@ -2682,49 +2693,12 @@ mch_setperm( void mch_hide(char_u *name) { - int perm; -#ifdef FEAT_MBYTE - WCHAR *p = NULL; - - if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) - p = enc_to_utf16(name, NULL); -#endif - -#ifdef FEAT_MBYTE - if (p != NULL) - { - perm = GetFileAttributesW(p); - if (perm < 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) - { - /* Retry with non-wide function (for Windows 98). */ - vim_free(p); - p = NULL; - } - } - if (p == NULL) -#endif - perm = GetFileAttributes((char *)name); - if (perm >= 0) - { - perm |= FILE_ATTRIBUTE_HIDDEN; -#ifdef FEAT_MBYTE - if (p != NULL) - { - if (SetFileAttributesW(p, perm) == 0 - && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) - { - /* Retry with non-wide function (for Windows 98). */ - vim_free(p); - p = NULL; - } - } - if (p == NULL) -#endif - SetFileAttributes((char *)name, perm); - } -#ifdef FEAT_MBYTE - vim_free(p); -#endif + int attrs = win32_getattrs(name); + if (attrs == -1) + return; + + attrs |= FILE_ATTRIBUTE_HIDDEN; + win32_setattrs(name, attrs); } /* @@ -2734,7 +2708,7 @@ mch_hide(char_u *name) int mch_isdir(char_u *name) { - int f = mch_getperm(name); + int f = win32_getattrs(name); if (f == -1) return FALSE; /* file does not exist at all */ @@ -2770,7 +2744,7 @@ mch_mkdir(char_u *name) * Return TRUE if file "fname" has more than one link. */ int -mch_is_linked(char_u *fname) +mch_is_hard_link(char_u *fname) { BY_HANDLE_FILE_INFORMATION info; @@ -2779,6 +2753,74 @@ mch_is_linked(char_u *fname) } /* + * Return TRUE if file "fname" is a symbolic link. + */ + int +mch_is_symbolic_link(char_u *fname) +{ + HANDLE hFind; + int res = FALSE; + WIN32_FIND_DATAA findDataA; + DWORD fileFlags = 0, reparseTag = 0; +#ifdef FEAT_MBYTE + WCHAR *wn = NULL; + WIN32_FIND_DATAW findDataW; + + if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) + wn = enc_to_utf16(fname, NULL); + if (wn != NULL) + { + hFind = FindFirstFileW(wn, &findDataW); + vim_free(wn); + if (hFind == INVALID_HANDLE_VALUE + && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) + { + /* Retry with non-wide function (for Windows 98). */ + hFind = FindFirstFile(fname, &findDataA); + if (hFind != INVALID_HANDLE_VALUE) + { + fileFlags = findDataA.dwFileAttributes; + reparseTag = findDataA.dwReserved0; + } + } + else + { + fileFlags = findDataW.dwFileAttributes; + reparseTag = findDataW.dwReserved0; + } + } +#else + hFind = FindFirstFile(fname, &findDataA); + if (hFind != INVALID_HANDLE_VALUE) + { + fileFlags = findDataA.dwFileAttributes; + reparseTag = findDataA.dwReserved0; + } +#endif + + if (hFind != INVALID_HANDLE_VALUE) + FindClose(hFind); + + if ((fileFlags & FILE_ATTRIBUTE_REPARSE_POINT) + && reparseTag == IO_REPARSE_TAG_SYMLINK) + res = TRUE; + + return res; +} + +/* + * Return TRUE if file "fname" has more than one link or if it is a symbolic + * link. + */ + int +mch_is_linked(char_u *fname) +{ + if (mch_is_hard_link(fname) || mch_is_symbolic_link(fname)) + return TRUE; + return FALSE; +} + +/* * Get the by-handle-file-information for "fname". * Returns FILEINFO_OK when OK. * returns FILEINFO_ENC_FAIL when enc_to_utf16() failed. @@ -2842,6 +2884,92 @@ win32_fileinfo(char_u *fname, BY_HANDLE_ } /* + * get file attributes for `name' + * -1 : error + * else FILE_ATTRIBUTE_* defined in winnt.h + */ + static + int +win32_getattrs(char_u *name) +{ + int attr; +#ifdef FEAT_MBYTE + WCHAR *p = NULL; + + if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) + p = enc_to_utf16(name, NULL); + + if (p != NULL) + { + attr = GetFileAttributesW(p); + if (attr < 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) + { + /* Retry with non-wide function (for Windows 98). */ + vim_free(p); + p = NULL; + } + } + if (p == NULL) +#endif + attr = GetFileAttributes((char *)name); +#ifdef FEAT_MBYTE + vim_free(p); +#endif + return attr; +} + +/* + * set file attributes for `name' to `attrs' + * + * return -1 for failure, 0 otherwise + */ + static + int +win32_setattrs(char_u *name, int attrs) +{ + int res; +#ifdef FEAT_MBYTE + WCHAR *p = NULL; + + if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) + p = enc_to_utf16(name, NULL); + + if (p != NULL) + { + res = SetFileAttributesW(p, attrs); + if (res == FALSE + && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) + { + /* Retry with non-wide function (for Windows 98). */ + vim_free(p); + p = NULL; + } + } + if (p == NULL) +#endif + res = SetFileAttributes((char *)name, attrs); +#ifdef FEAT_MBYTE + vim_free(p); +#endif + return res ? 0 : -1; +} + +/* + * Set archive flag for "name". + */ + static + int +win32_set_archive(char_u *name) +{ + int attrs = win32_getattrs(name); + if (attrs == -1) + return -1; + + attrs |= FILE_ATTRIBUTE_ARCHIVE; + return win32_setattrs(name, attrs); +} + +/* * Return TRUE if file or directory "name" is writable (not readonly). * Strange semantics of Win32: a readonly directory is writable, but you can't * delete a file. Let's say this means it is writable. @@ -2849,10 +2977,10 @@ win32_fileinfo(char_u *fname, BY_HANDLE_ int mch_writable(char_u *name) { - int perm = mch_getperm(name); - - return (perm != -1 && (!(perm & FILE_ATTRIBUTE_READONLY) - || (perm & FILE_ATTRIBUTE_DIRECTORY))); + int attrs = win32_getattrs(name); + + return (attrs != -1 && (!(attrs & FILE_ATTRIBUTE_READONLY) + || (attrs & FILE_ATTRIBUTE_DIRECTORY))); } /* @@ -5012,13 +5140,16 @@ mch_remove(char_u *name) #ifdef FEAT_MBYTE WCHAR *wn = NULL; int n; - +#endif + + win32_setattrs(name, FILE_ATTRIBUTE_NORMAL); + +#ifdef FEAT_MBYTE if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) { wn = enc_to_utf16(name, NULL); if (wn != NULL) { - SetFileAttributesW(wn, FILE_ATTRIBUTE_NORMAL); n = DeleteFileW(wn) ? 0 : -1; vim_free(wn); if (n == 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) @@ -5027,7 +5158,6 @@ mch_remove(char_u *name) } } #endif - SetFileAttributes(name, FILE_ATTRIBUTE_NORMAL); return DeleteFile(name) ? 0 : -1; } diff --git a/src/proto/os_win32.pro b/src/proto/os_win32.pro --- a/src/proto/os_win32.pro +++ b/src/proto/os_win32.pro @@ -21,6 +21,8 @@ int mch_setperm __ARGS((char_u *name, lo void mch_hide __ARGS((char_u *name)); int mch_isdir __ARGS((char_u *name)); int mch_mkdir __ARGS((char_u *name)); +int mch_is_hard_link __ARGS((char_u *fname)); +int mch_is_symbolic_link __ARGS((char_u *fname)); int mch_is_linked __ARGS((char_u *fname)); int win32_fileinfo __ARGS((char_u *fname, BY_HANDLE_FILE_INFORMATION *info)); int mch_writable __ARGS((char_u *name)); diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1182, +/**/ 1181, /**/ 1180,