1 /****************************************************************************
2 * fs/vfs/fs_rename.c
3 *
4 * Copyright (C) 2007-2009, 2014, 2017 Gregory Nutt. All rights reserved.
5 * Author: Gregory Nutt <gnutt@nuttx.org>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name NuttX nor the names of its contributors may be
18 * used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
28 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *
34 ****************************************************************************/
35
36 /****************************************************************************
37 * Included Files
38 ****************************************************************************/
39
40 #include "vfs_config.h"
41
42 #include "stdio.h"
43 #include "unistd.h"
44 #include "errno.h"
45 #include "stdlib.h"
46 #include "vnode.h"
47 #include "limits.h"
48 #include "fs/fs_operation.h"
49 #include "path_cache.h"
50 /****************************************************************************
51 * Public Functions
52 ****************************************************************************/
check_rename_target(struct Vnode * old_vnode,struct Vnode * old_parent_vnode,struct Vnode * new_vnode,struct Vnode * new_parent_vnode)53 static int check_rename_target(struct Vnode *old_vnode, struct Vnode *old_parent_vnode,
54 struct Vnode *new_vnode, struct Vnode *new_parent_vnode)
55 {
56 if (old_vnode == NULL || old_parent_vnode == NULL ||
57 new_parent_vnode == NULL || new_parent_vnode->type != VNODE_TYPE_DIR)
58 {
59 return -ENOENT;
60 }
61 if ((new_parent_vnode->originMount) && (new_parent_vnode->originMount->mountFlags & MS_RDONLY))
62 {
63 return -EROFS;
64 }
65 if (old_vnode->type != VNODE_TYPE_DIR && old_vnode->type != VNODE_TYPE_REG)
66 {
67 return -EACCES;
68 }
69 if (new_vnode != NULL && new_vnode->type != old_vnode->type)
70 {
71 if (new_vnode->type == VNODE_TYPE_DIR)
72 {
73 return -EISDIR;
74 }
75 return -ENOTDIR;
76 }
77 if (new_vnode != NULL && new_vnode->useCount != 0)
78 {
79 return -EBUSY;
80 }
81
82 if (VfsVnodePermissionCheck(old_parent_vnode, (WRITE_OP | EXEC_OP))
83 || VfsVnodePermissionCheck(new_parent_vnode, (WRITE_OP | EXEC_OP)))
84 {
85 return -EACCES;
86 }
87
88 if (old_parent_vnode->originMount != new_parent_vnode->originMount)
89 {
90 return -EXDEV;
91 }
92 if ((old_vnode->flag & VNODE_FLAG_MOUNT_ORIGIN)
93 || (old_vnode->flag & VNODE_FLAG_MOUNT_NEW))
94 {
95 return -EBUSY;
96 }
97 if (new_vnode != NULL && ((new_vnode->flag & VNODE_FLAG_MOUNT_ORIGIN)
98 || (new_vnode->flag & VNODE_FLAG_MOUNT_NEW)))
99 {
100 return -EBUSY;
101 }
102
103 return OK;
104 }
105
check_path_invalid(const char * fulloldpath,const char * fullnewpath)106 static int check_path_invalid(const char *fulloldpath, const char *fullnewpath)
107 {
108 char cwd[PATH_MAX];
109 char *pret = getcwd(cwd, PATH_MAX);
110 ssize_t len = strlen(fulloldpath);
111 if (pret != NULL)
112 {
113 if (!strcmp(fulloldpath, cwd))
114 {
115 return -EBUSY;
116 }
117 }
118
119 if (strncmp(fulloldpath, fullnewpath, len))
120 {
121 return OK;
122 }
123
124 if (fullnewpath[len] != '/')
125 {
126 return OK;
127 }
128
129 return -EINVAL;
130 }
131
do_rename(int oldfd,const char * oldpath,int newfd,const char * newpath)132 int do_rename(int oldfd, const char *oldpath, int newfd, const char *newpath)
133 {
134 struct Vnode *old_parent_vnode = NULL;
135 struct Vnode *new_parent_vnode = NULL;
136 struct Vnode *old_vnode = NULL;
137 struct Vnode *new_vnode = NULL;
138 char *fulloldpath = NULL;
139 char *fullnewpath = NULL;
140 char *oldname = NULL;
141 char *newname = NULL;
142 int ret;
143 if (!oldpath || *oldpath == '\0' || !newpath || *newpath == '\0')
144 {
145 ret = -EINVAL;
146 goto errout;
147 }
148
149 ret = vfs_normalize_pathat(oldfd, oldpath, &fulloldpath);
150 if (ret < 0)
151 {
152 goto errout;
153 }
154
155 ret = vfs_normalize_pathat(newfd, newpath, &fullnewpath);
156 if (ret < 0)
157 {
158 goto errout_with_oldpath;
159 }
160 oldname = strrchr(fulloldpath, '/') + 1;
161 newname = strrchr(fullnewpath, '/') + 1;
162 ret = check_path_invalid(fulloldpath, fullnewpath);
163 if (ret != OK)
164 {
165 goto errout_with_newpath;
166 }
167
168 VnodeHold();
169 ret = VnodeLookup(fulloldpath, &old_vnode, 0);
170 if (ret < 0)
171 {
172 goto errout_with_vnode;
173 }
174 old_parent_vnode = old_vnode->parent;
175 ret = VnodeLookup(fullnewpath, &new_vnode, 0);
176 if (ret == OK)
177 {
178 new_parent_vnode = new_vnode->parent;
179 }
180 else
181 {
182 new_parent_vnode = new_vnode;
183 new_vnode = NULL;
184 }
185 ret = check_rename_target(old_vnode, old_parent_vnode, new_vnode, new_parent_vnode);
186 if (ret != OK)
187 {
188 goto errout_with_vnode;
189 }
190 if (old_vnode == new_vnode)
191 {
192 VnodeDrop();
193 free(fulloldpath);
194 free(fullnewpath);
195 return OK;
196 }
197 if (!old_vnode->vop || !old_vnode->vop->Rename)
198 {
199 ret = -ENOSYS;
200 goto errout_with_vnode;
201 }
202 new_parent_vnode->useCount++;
203 ret = old_vnode->vop->Rename(old_vnode, new_parent_vnode, oldname, newname);
204 new_parent_vnode->useCount--;
205 if (ret < 0)
206 {
207 goto errout_with_vnode;
208 }
209 VnodeFree(new_vnode);
210 VnodePathCacheFree(old_vnode);
211 old_vnode->filePath = strdup(fullnewpath);
212 PathCacheAlloc(new_parent_vnode, old_vnode, newname, strlen(newname));
213 old_vnode->parent = new_parent_vnode;
214 VnodeDrop();
215
216 free(fulloldpath);
217 free(fullnewpath);
218
219 return OK;
220
221 errout_with_vnode:
222 VnodeDrop();
223 errout_with_newpath:
224 free(fullnewpath);
225 errout_with_oldpath:
226 free(fulloldpath);
227 errout:
228 set_errno(-ret);
229 return VFS_ERROR;
230 }
231
232
233 /****************************************************************************
234 * Name: rename
235 *
236 * Description: Rename a file managed a mountpoint
237 *
238 ****************************************************************************/
239
rename(const char * oldpath,const char * newpath)240 int rename(const char *oldpath, const char *newpath)
241 {
242 return do_rename(AT_FDCWD, oldpath, AT_FDCWD, newpath);
243 }
244
245 /****************************************************************************
246 * Name: renameat
247 *
248 * Description: Rename a file managed a mountpoint with relatively fds.
249 *
250 ****************************************************************************/
251
renameat(int oldfd,const char * oldpath,int newdfd,const char * newpath)252 int renameat(int oldfd, const char *oldpath, int newdfd, const char *newpath)
253 {
254 return do_rename(oldfd, oldpath, newdfd, newpath);
255 }
256