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