1 /* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    Without limiting anything contained in the foregoing, this file,
15    which is part of C Driver for MySQL (Connector/C), is also subject to the
16    Universal FOSS Exception, version 1.0, a copy of which can be found at
17    http://oss.oracle.com/licenses/universal-foss-exception.
18 
19    This program is distributed in the hope that it will be useful,
20    but WITHOUT ANY WARRANTY; without even the implied warranty of
21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22    GNU General Public License, version 2.0, for more details.
23 
24    You should have received a copy of the GNU General Public License
25    along with this program; if not, write to the Free Software
26    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
27 
28 /*
29   Advanced symlink handling.
30   This is used in MyISAM to let users symlinks tables to different disk.
31   The main idea with these functions is to automaticly create, delete and
32   rename files and symlinks like they would be one unit.
33 */
34 
35 #include "mysys_priv.h"
36 #include "mysys_err.h"
37 #include <m_string.h>
38 
my_create_with_symlink(const char * linkname,const char * filename,int createflags,int access_flags,myf MyFlags)39 File my_create_with_symlink(const char *linkname, const char *filename,
40 			    int createflags, int access_flags, myf MyFlags)
41 {
42   File file;
43   int tmp_errno;
44   /* Test if we should create a link */
45   int create_link;
46   char abs_linkname[FN_REFLEN];
47   DBUG_ENTER("my_create_with_symlink");
48   DBUG_PRINT("enter", ("linkname: %s  filename: %s",
49                        linkname ? linkname : "(null)",
50                        filename ? filename : "(null)"));
51 
52   if (my_disable_symlinks)
53   {
54     DBUG_PRINT("info", ("Symlinks disabled"));
55     /* Create only the file, not the link and file */
56     create_link= 0;
57     if (linkname)
58       filename= linkname;
59   }
60   else
61   {
62     if (linkname)
63       my_realpath(abs_linkname, linkname, MYF(0));
64     create_link= (linkname && strcmp(abs_linkname,filename));
65   }
66 
67   if (!(MyFlags & MY_DELETE_OLD))
68   {
69     if (!access(filename,F_OK))
70     {
71       char errbuf[MYSYS_STRERROR_SIZE];
72       my_errno= errno= EEXIST;
73       my_error(EE_CANTCREATEFILE, MYF(0), filename,
74                EEXIST, my_strerror(errbuf, sizeof(errbuf), EEXIST));
75       DBUG_RETURN(-1);
76     }
77     if (create_link && !access(linkname,F_OK))
78     {
79       char errbuf[MYSYS_STRERROR_SIZE];
80       my_errno= errno= EEXIST;
81       my_error(EE_CANTCREATEFILE, MYF(0), linkname,
82                EEXIST, my_strerror(errbuf, sizeof(errbuf), EEXIST));
83       DBUG_RETURN(-1);
84     }
85   }
86 
87   if ((file=my_create(filename, createflags, access_flags, MyFlags)) >= 0)
88   {
89     if (create_link)
90     {
91       /* Delete old link/file */
92       if (MyFlags & MY_DELETE_OLD)
93 	my_delete(linkname, MYF(0));
94       /* Create link */
95       if (my_symlink(filename, linkname, MyFlags))
96       {
97 	/* Fail, remove everything we have done */
98 	tmp_errno=my_errno;
99 	my_close(file,MYF(0));
100 	my_delete(filename, MYF(0));
101 	file= -1;
102 	my_errno=tmp_errno;
103       }
104     }
105   }
106   DBUG_RETURN(file);
107 }
108 
109 /*
110   If the file was a symlink, delete both symlink and the file which the
111   symlink pointed to.
112 */
113 
my_delete_with_symlink(const char * name,myf MyFlags)114 int my_delete_with_symlink(const char *name, myf MyFlags)
115 {
116   char link_name[FN_REFLEN];
117   int was_symlink= (!my_disable_symlinks &&
118 		    !my_readlink(link_name, name, MYF(0)));
119   int result;
120   DBUG_ENTER("my_delete_with_symlink");
121 
122   if (!(result=my_delete(name, MyFlags)))
123   {
124     if (was_symlink)
125       result=my_delete(link_name, MyFlags);
126   }
127   DBUG_RETURN(result);
128 }
129 
130 /*
131   If the file is a normal file, just rename it.
132   If the file is a symlink:
133    - Create a new file with the name 'to' that points at
134      symlink_dir/basename(to)
135    - Rename the symlinked file to symlink_dir/basename(to)
136    - Delete 'from'
137    If something goes wrong, restore everything.
138 */
139 
my_rename_with_symlink(const char * from,const char * to,myf MyFlags)140 int my_rename_with_symlink(const char *from, const char *to, myf MyFlags)
141 {
142 #ifndef HAVE_READLINK
143   return my_rename(from, to, MyFlags);
144 #else
145   char link_name[FN_REFLEN], tmp_name[FN_REFLEN];
146   int was_symlink= (!my_disable_symlinks &&
147 		    !my_readlink(link_name, from, MYF(0)));
148   int result=0;
149   int name_is_different;
150   DBUG_ENTER("my_rename_with_symlink");
151 
152   if (!was_symlink)
153     DBUG_RETURN(my_rename(from, to, MyFlags));
154 
155   /* Change filename that symlink pointed to */
156   strmov(tmp_name, to);
157   fn_same(tmp_name,link_name,1);		/* Copy dir */
158   name_is_different= strcmp(link_name, tmp_name);
159   if (name_is_different && !access(tmp_name, F_OK))
160   {
161     my_errno= EEXIST;
162     if (MyFlags & MY_WME)
163     {
164       char errbuf[MYSYS_STRERROR_SIZE];
165       my_error(EE_CANTCREATEFILE, MYF(0), tmp_name,
166                EEXIST, my_strerror(errbuf, sizeof(errbuf), EEXIST));
167     }
168     DBUG_RETURN(1);
169   }
170 
171   /* Create new symlink */
172   if (my_symlink(tmp_name, to, MyFlags))
173     DBUG_RETURN(1);
174 
175   /*
176     Rename symlinked file if the base name didn't change.
177     This can happen if you use this function where 'from' and 'to' has
178     the same basename and different directories.
179    */
180 
181   if (name_is_different && my_rename(link_name, tmp_name, MyFlags))
182   {
183     int save_errno=my_errno;
184     my_delete(to, MyFlags);			/* Remove created symlink */
185     my_errno=save_errno;
186     DBUG_RETURN(1);
187   }
188 
189   /* Remove original symlink */
190   if (my_delete(from, MyFlags))
191   {
192     int save_errno=my_errno;
193     /* Remove created link */
194     my_delete(to, MyFlags);
195     /* Rename file back */
196     if (strcmp(link_name, tmp_name))
197       (void) my_rename(tmp_name, link_name, MyFlags);
198     my_errno=save_errno;
199     result= 1;
200   }
201   DBUG_RETURN(result);
202 #endif /* HAVE_READLINK */
203 }
204