xref: /third_party/NuttX/fs/vfs/fs_link.c (revision beacf11b)
1/*
2 * Copyright (c) 2021-2021 Huawei Device Co., Ltd. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification,
5 * are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this list of
8 *    conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 *    of conditions and the following disclaimer in the documentation and/or other materials
12 *    provided with the distribution.
13 *
14 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
15 *    to endorse or promote products derived from this software without specific prior written
16 *    permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
22 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
27 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
28 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "unistd.h"
32#include "errno.h"
33#include "vnode.h"
34#include "path_cache.h"
35
36int do_link(int oldfd, const char *oldpath, int newfd, const char *newpath, int flag)
37{
38  struct Vnode *new_parent_vnode = NULL;
39  struct Vnode *new_vnode = NULL;
40  struct Vnode *old_vnode = NULL;
41  struct Mount *mount = NULL;
42  char *fulloldpath = NULL;
43  char *fullnewpath = NULL;
44  char *newname = NULL;
45  int ret;
46
47  if (!oldpath || !newpath)
48    {
49      ret = -EFAULT;
50      goto errout;
51    }
52
53  if (*oldpath == '\0' || *newpath == '\0' || flag & ~AT_SYMLINK_FOLLOW)
54    {
55      ret = -EINVAL;
56      goto errout;
57    }
58
59  ret = vfs_normalize_pathat(newfd, newpath, &fullnewpath);
60  if (ret < 0)
61    {
62      goto errout;
63    }
64
65  if (!(flag & AT_SYMLINK_FOLLOW))
66    {
67      ret = vfs_normalize_pathat(oldfd, oldpath, &fulloldpath);
68      if (ret < 0)
69        {
70          goto errout_with_newpath;
71        }
72    }
73
74  newname = strrchr(fullnewpath, '/') + 1;
75
76  VnodeHold();
77
78  if (flag & AT_SYMLINK_FOLLOW)
79    {
80      ret = follow_symlink(oldfd, oldpath, &old_vnode, &fulloldpath);
81      if (ret < 0)
82        {
83          goto errout_with_vnode;
84        }
85    }
86  else
87    {
88      ret = VnodeLookup(fulloldpath, &old_vnode, 0);
89      if (ret < 0)
90        {
91          goto errout_with_vnode;
92        }
93    }
94
95  mount = old_vnode->originMount;
96  if ((mount != NULL) && (mount->mountFlags & MS_RDONLY))
97    {
98      ret = -EROFS;
99      goto errout_with_vnode;
100    }
101
102  if (old_vnode->type != VNODE_TYPE_REG && old_vnode->type != VNODE_TYPE_LNK)
103    {
104      ret = -EPERM;
105      goto errout_with_vnode;
106    }
107
108  ret = VnodeLookup(fullnewpath, &new_parent_vnode, 0);
109  if (ret == OK)
110    {
111      ret = -EEXIST;
112      goto errout_with_vnode;
113    }
114
115  if (old_vnode->originMount != new_parent_vnode->originMount)
116    {
117      ret = -EXDEV;
118      goto errout_with_vnode;
119    }
120
121  if (!old_vnode->vop || !old_vnode->vop->Link)
122    {
123      ret = -ENOSYS;
124      goto errout_with_vnode;
125    }
126  new_parent_vnode->useCount++;
127  ret = old_vnode->vop->Link(old_vnode, new_parent_vnode, &new_vnode, newname);
128  new_parent_vnode->useCount--;
129  if (ret < 0)
130    {
131      goto errout_with_vnode;
132    }
133  PathCacheAlloc(new_parent_vnode, new_vnode, newname, strlen(newname));
134  VnodeDrop();
135
136  free(fulloldpath);
137  free(fullnewpath);
138
139  return OK;
140
141errout_with_vnode:
142  VnodeDrop();
143  free(fulloldpath);
144errout_with_newpath:
145  free(fullnewpath);
146errout:
147  set_errno(-ret);
148  return VFS_ERROR;
149}
150
151int link(const char *oldpath, const char *newpath)
152{
153  return do_link(AT_FDCWD, oldpath, AT_FDCWD, newpath, 0);
154}
155
156int linkat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath, int flags)
157{
158  return do_link(olddirfd, oldpath, newdirfd, newpath, flags);
159}
160