Blob Blame History Raw
To: vim_dev@googlegroups.com
Subject: Patch 7.3.1182
Fcc: outbox
From: Bram Moolenaar <Bram@moolenaar.net>
Mime-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
------------

Patch 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)
Files:	    src/fileio.c, src/os_win32.c, src/proto/os_win32.pro


*** ../vim-7.3.1181/src/fileio.c	2013-06-12 19:52:11.000000000 +0200
--- src/fileio.c	2013-06-12 22:31:34.000000000 +0200
***************
*** 3780,3791 ****
  	    }
  	}
  
- # ifdef UNIX
  	/*
  	 * Break symlinks and/or hardlinks if we've been asked to.
  	 */
  	if ((bkc_flags & BKC_BREAKSYMLINK) || (bkc_flags & BKC_BREAKHARDLINK))
  	{
  	    int	lstat_res;
  
  	    lstat_res = mch_lstat((char *)fname, &st);
--- 3780,3791 ----
  	    }
  	}
  
  	/*
  	 * 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,3808 ****
  		    && st_old.st_nlink > 1
  		    && (lstat_res != 0 || st.st_ino == st_old.st_ino))
  		backup_copy = FALSE;
  	}
- #endif
  
  #endif
  
--- 3801,3818 ----
  		    && st_old.st_nlink > 1
  		    && (lstat_res != 0 || st.st_ino == st_old.st_ino))
  		backup_copy = FALSE;
+ # 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
  
*** ../vim-7.3.1181/src/os_win32.c	2013-06-07 19:17:12.000000000 +0200
--- src/os_win32.c	2013-06-12 22:39:53.000000000 +0200
***************
*** 78,83 ****
--- 78,93 ----
  # 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,224 ****
--- 229,238 ----
  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,2679 ****
  /*
   * get file permissions for `name'
   * -1 : error
!  * else FILE_ATTRIBUTE_* defined in winnt.h
   */
      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);
  }
  
  
  /*
   * set file permission for `name' to `perm'
   */
      int
  mch_setperm(
      char_u  *name,
      long    perm)
  {
!     perm |= FILE_ATTRIBUTE_ARCHIVE;	/* file has changed, set archive bit */
  #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)SetFileAttributesW(p, perm);
  	    vim_free(p);
! 	    if (n || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
! 		return n ? OK : FAIL;
  	    /* Retry with non-wide function (for Windows 98). */
  	}
      }
  #endif
!     return SetFileAttributes((char *)name, perm) ? OK : FAIL;
  }
  
  /*
--- 2637,2690 ----
  /*
   * get file permissions for `name'
   * -1 : error
!  * else mode_t
   */
      long
  mch_getperm(char_u *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)
  {
!     long	n;
  #ifdef FEAT_MBYTE
+     WCHAR *p;
      if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
      {
! 	p = enc_to_utf16(name, NULL);
  
  	if (p != NULL)
  	{
! 	    n = _wchmod(p, perm);
  	    vim_free(p);
! 	    if (n == -1 && GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
! 		return FAIL;
  	    /* Retry with non-wide function (for Windows 98). */
  	}
      }
+     if (p == NULL)
  #endif
! 	n = _chmod(name, perm);
!     if (n == -1)
! 	return FAIL;
! 
!     win32_set_archive(name);
! 
!     return OK;
  }
  
  /*
***************
*** 2682,2730 ****
      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
  }
  
  /*
--- 2693,2704 ----
      void
  mch_hide(char_u *name)
  {
!     int attrs = win32_getattrs(name);
!     if (attrs == -1)
! 	return;
  
!     attrs |= FILE_ATTRIBUTE_HIDDEN;
!     win32_setattrs(name, attrs);
  }
  
  /*
***************
*** 2734,2740 ****
      int
  mch_isdir(char_u *name)
  {
!     int f = mch_getperm(name);
  
      if (f == -1)
  	return FALSE;		    /* file does not exist at all */
--- 2708,2714 ----
      int
  mch_isdir(char_u *name)
  {
!     int f = win32_getattrs(name);
  
      if (f == -1)
  	return FALSE;		    /* file does not exist at all */
***************
*** 2770,2776 ****
   * Return TRUE if file "fname" has more than one link.
   */
      int
! mch_is_linked(char_u *fname)
  {
      BY_HANDLE_FILE_INFORMATION info;
  
--- 2744,2750 ----
   * Return TRUE if file "fname" has more than one link.
   */
      int
! mch_is_hard_link(char_u *fname)
  {
      BY_HANDLE_FILE_INFORMATION info;
  
***************
*** 2779,2784 ****
--- 2753,2826 ----
  }
  
  /*
+  * 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,2847 ****
--- 2884,2975 ----
  }
  
  /*
+  * 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,2858 ****
      int
  mch_writable(char_u *name)
  {
!     int perm = mch_getperm(name);
  
!     return (perm != -1 && (!(perm & FILE_ATTRIBUTE_READONLY)
! 				       || (perm & FILE_ATTRIBUTE_DIRECTORY)));
  }
  
  /*
--- 2977,2986 ----
      int
  mch_writable(char_u *name)
  {
!     int attrs = win32_getattrs(name);
  
!     return (attrs != -1 && (!(attrs & FILE_ATTRIBUTE_READONLY)
! 			  || (attrs & FILE_ATTRIBUTE_DIRECTORY)));
  }
  
  /*
***************
*** 5012,5024 ****
  #ifdef FEAT_MBYTE
      WCHAR	*wn = NULL;
      int		n;
  
      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)
--- 5140,5155 ----
  #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)
  	{
  	    n = DeleteFileW(wn) ? 0 : -1;
  	    vim_free(wn);
  	    if (n == 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
***************
*** 5027,5033 ****
  	}
      }
  #endif
-     SetFileAttributes(name, FILE_ATTRIBUTE_NORMAL);
      return DeleteFile(name) ? 0 : -1;
  }
  
--- 5158,5163 ----
*** ../vim-7.3.1181/src/proto/os_win32.pro	2012-11-20 16:56:49.000000000 +0100
--- src/proto/os_win32.pro	2013-06-12 22:29:53.000000000 +0200
***************
*** 21,26 ****
--- 21,28 ----
  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));
*** ../vim-7.3.1181/src/version.c	2013-06-12 22:08:54.000000000 +0200
--- src/version.c	2013-06-12 22:40:29.000000000 +0200
***************
*** 730,731 ****
--- 730,733 ----
  {   /* Add new patch number below this line */
+ /**/
+     1182,
  /**/

-- 
If Microsoft would build a car...
... You'd have to press the "Start" button to turn the engine off.

 /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///