xref: /third_party/NuttX/fs/vfs/fs_rename.c (revision beacf11b)
1/****************************************************************************
2 * fs/vfs/fs_rename.c
3 *
4 * Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved.
5 * Based on NuttX originally from nuttx source (nuttx/fs/ and nuttx/drivers/)
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *     http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 ****************************************************************************/
20
21/****************************************************************************
22 * Included Files
23 ****************************************************************************/
24
25#include "vfs_config.h"
26
27#include "stdio.h"
28#include "unistd.h"
29#include "errno.h"
30#include "stdlib.h"
31#include "vnode.h"
32#include "limits.h"
33#include "fs/fs_operation.h"
34#include "path_cache.h"
35/****************************************************************************
36 * Public Functions
37 ****************************************************************************/
38static int check_rename_target(struct Vnode *old_vnode, struct Vnode *old_parent_vnode,
39    struct Vnode *new_vnode, struct Vnode *new_parent_vnode)
40{
41  if (old_vnode == NULL || old_parent_vnode == NULL ||
42      new_parent_vnode == NULL || new_parent_vnode->type != VNODE_TYPE_DIR)
43    {
44      return -ENOENT;
45    }
46  if ((new_parent_vnode->originMount) && (new_parent_vnode->originMount->mountFlags & MS_RDONLY))
47    {
48      return -EROFS;
49    }
50  if (old_vnode->type != VNODE_TYPE_DIR && old_vnode->type != VNODE_TYPE_REG)
51    {
52      return -EACCES;
53    }
54  if (new_vnode != NULL && new_vnode->type != old_vnode->type)
55    {
56      if (new_vnode->type == VNODE_TYPE_DIR)
57        {
58          return -EISDIR;
59        }
60      return -ENOTDIR;
61    }
62  if (new_vnode != NULL && new_vnode->useCount != 0)
63    {
64      return -EBUSY;
65    }
66
67  if (VfsVnodePermissionCheck(old_parent_vnode, (WRITE_OP | EXEC_OP))
68      || VfsVnodePermissionCheck(new_parent_vnode, (WRITE_OP | EXEC_OP)))
69    {
70      return -EACCES;
71    }
72
73  if (old_parent_vnode->originMount != new_parent_vnode->originMount)
74    {
75      return -EXDEV;
76    }
77  if ((old_vnode->flag & VNODE_FLAG_MOUNT_ORIGIN)
78       || (old_vnode->flag & VNODE_FLAG_MOUNT_NEW))
79    {
80      return -EBUSY;
81    }
82  if (new_vnode != NULL && ((new_vnode->flag & VNODE_FLAG_MOUNT_ORIGIN)
83      || (new_vnode->flag & VNODE_FLAG_MOUNT_NEW)))
84    {
85      return -EBUSY;
86    }
87
88  return OK;
89}
90
91static int check_path_invalid(const char *fulloldpath, const char *fullnewpath)
92{
93  char cwd[PATH_MAX];
94  char *pret = getcwd(cwd, PATH_MAX);
95  ssize_t len = strlen(fulloldpath);
96  if (pret != NULL)
97    {
98      if (!strcmp(fulloldpath, cwd))
99        {
100          return -EBUSY;
101        }
102    }
103
104  if (strncmp(fulloldpath, fullnewpath, len))
105    {
106      return OK;
107    }
108
109  if (fullnewpath[len] != '/')
110    {
111      return OK;
112    }
113
114  return -EINVAL;
115}
116
117int do_rename(int oldfd, const char *oldpath, int newfd, const char *newpath)
118{
119  struct Vnode *old_parent_vnode = NULL;
120  struct Vnode *new_parent_vnode = NULL;
121  struct Vnode *old_vnode = NULL;
122  struct Vnode *new_vnode = NULL;
123  char *fulloldpath = NULL;
124  char *fullnewpath = NULL;
125  char *oldname = NULL;
126  char *newname = NULL;
127  int ret;
128  if (!oldpath || *oldpath == '\0' || !newpath || *newpath == '\0')
129    {
130      ret = -EINVAL;
131      goto errout;
132    }
133
134  ret = vfs_normalize_pathat(oldfd, oldpath, &fulloldpath);
135  if (ret < 0)
136    {
137      goto errout;
138    }
139
140  ret = vfs_normalize_pathat(newfd, newpath, &fullnewpath);
141  if (ret < 0)
142    {
143      goto errout_with_oldpath;
144    }
145  oldname = strrchr(fulloldpath, '/') + 1;
146  newname = strrchr(fullnewpath, '/') + 1;
147  ret = check_path_invalid(fulloldpath, fullnewpath);
148  if (ret != OK)
149    {
150      goto errout_with_newpath;
151    }
152
153  VnodeHold();
154  ret = VnodeLookup(fulloldpath, &old_vnode, 0);
155  if (ret < 0)
156    {
157      goto errout_with_vnode;
158    }
159  old_parent_vnode = old_vnode->parent;
160  ret = VnodeLookup(fullnewpath, &new_vnode, 0);
161  if (ret == OK)
162    {
163      new_parent_vnode = new_vnode->parent;
164    }
165  else
166    {
167      new_parent_vnode = new_vnode;
168      new_vnode = NULL;
169    }
170  ret = check_rename_target(old_vnode, old_parent_vnode, new_vnode, new_parent_vnode);
171  if (ret != OK)
172    {
173      goto errout_with_vnode;
174    }
175  if (old_vnode == new_vnode)
176    {
177      VnodeDrop();
178      free(fulloldpath);
179      free(fullnewpath);
180      return OK;
181    }
182  if (!old_vnode->vop || !old_vnode->vop->Rename)
183    {
184      ret = -ENOSYS;
185      goto errout_with_vnode;
186    }
187  new_parent_vnode->useCount++;
188  ret = old_vnode->vop->Rename(old_vnode, new_parent_vnode, oldname, newname);
189  new_parent_vnode->useCount--;
190  if (ret < 0)
191    {
192      goto errout_with_vnode;
193    }
194  VnodeFree(new_vnode);
195  VnodePathCacheFree(old_vnode);
196  old_vnode->filePath = strdup(fullnewpath);
197  PathCacheAlloc(new_parent_vnode, old_vnode, newname, strlen(newname));
198  old_vnode->parent = new_parent_vnode;
199  VnodeDrop();
200
201  free(fulloldpath);
202  free(fullnewpath);
203
204  return OK;
205
206errout_with_vnode:
207  VnodeDrop();
208errout_with_newpath:
209  free(fullnewpath);
210errout_with_oldpath:
211  free(fulloldpath);
212errout:
213  set_errno(-ret);
214  return VFS_ERROR;
215}
216
217
218/****************************************************************************
219 * Name: rename
220 *
221 * Description:  Rename a file managed a mountpoint
222 *
223 ****************************************************************************/
224
225int rename(const char *oldpath, const char *newpath)
226{
227  return do_rename(AT_FDCWD, oldpath, AT_FDCWD, newpath);
228}
229
230/****************************************************************************
231 * Name: renameat
232 *
233 * Description:  Rename a file managed a mountpoint with relatively fds.
234 *
235 ****************************************************************************/
236
237int renameat(int oldfd, const char *oldpath, int newdfd, const char *newpath)
238{
239  return do_rename(oldfd, oldpath, newdfd, newpath);
240}
241