1 /****************************************************************************
2 * fs/vfs/fs_open.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 "errno.h"
28 #include "sys/types.h"
29 #include "fcntl.h"
30 #include "sched.h"
31 #include "assert.h"
32 #ifdef LOSCFG_FILE_MODE
33 #include "stdarg.h"
34 #endif
35 #include "stdlib.h"
36 #include "vnode.h"
37 #include "blockproxy.h"
38 #include "path_cache.h"
39 #include "unistd.h"
40 #ifdef LOSCFG_KERNEL_DEV_PLIMIT
41 #include "los_plimits.h"
42 #endif
43
44 /****************************************************************************
45 * Public Functions
46 ****************************************************************************/
47
oflag_convert_mode(int oflags)48 static int oflag_convert_mode(int oflags)
49 {
50 /* regular file operations */
51
52 int acc_mode = 0;
53 if ((oflags & O_ACCMODE) == O_RDONLY)
54 acc_mode |= READ_OP;
55 if (oflags & O_WRONLY)
56 acc_mode |= WRITE_OP;
57 if (oflags & O_RDWR)
58 acc_mode |= READ_OP | WRITE_OP;
59
60 /* Opens the file, if it is existing. If not, a new file is created. */
61
62 if (oflags & O_CREAT)
63 acc_mode |= WRITE_OP;
64
65 /* Creates a new file. If the file is existing, it is truncated and overwritten. */
66
67 if (oflags & O_TRUNC)
68 acc_mode |= WRITE_OP;
69
70 /* Creates a new file. The function fails if the file is already existing. */
71
72 if (oflags & O_EXCL)
73 acc_mode |= WRITE_OP;
74 if (oflags & O_APPEND)
75 acc_mode |= WRITE_OP;
76
77 /* mark for executing operation */
78
79 if (oflags & O_EXECVE)
80 acc_mode |= EXEC_OP;
81 return acc_mode;
82 }
83
get_path_from_fd(int fd,char ** path)84 int get_path_from_fd(int fd, char **path)
85 {
86 struct file *file = NULL;
87 char *copypath = NULL;
88
89 if (fd == AT_FDCWD)
90 {
91 return OK;
92 }
93
94 int ret = fs_getfilep(fd, &file);
95 if (ret < 0)
96 {
97 return -ENOENT;
98 }
99
100 if ((file == NULL) || (file->f_vnode == NULL) || (file->f_path == NULL))
101 {
102 return -EBADF;
103 }
104
105 copypath = strdup((const char*)file->f_path);
106 if (copypath == NULL)
107 {
108 return VFS_ERROR;
109 }
110
111 *path = copypath;
112 return OK;
113 }
114
do_creat(struct Vnode * parentNode,char * fullpath,mode_t mode,struct Vnode ** node)115 static int do_creat(struct Vnode *parentNode, char *fullpath, mode_t mode, struct Vnode **node)
116 {
117 int ret;
118 char *name = strrchr(fullpath, '/') + 1;
119 parentNode->useCount++;
120
121 if (parentNode->vop != NULL && parentNode->vop->Create != NULL)
122 {
123 ret = parentNode->vop->Create(parentNode, name, mode, node);
124 }
125 else
126 {
127 ret = -ENOSYS;
128 }
129
130 parentNode->useCount--;
131 if (ret < 0)
132 {
133 return ret;
134 }
135
136 struct PathCache *dt = PathCacheAlloc(parentNode, *node, name, strlen(name));
137 if (dt == NULL)
138 {
139 // alloc name cache failed is not a critical problem, let it go.
140 PRINT_ERR("alloc path cache %s failed\n", name);
141 }
142 return OK;
143 }
144
fp_open(int dirfd,const char * path,int oflags,mode_t mode)145 int fp_open(int dirfd, const char *path, int oflags, mode_t mode)
146 {
147 int ret;
148 int accmode;
149 struct file *filep = NULL;
150 struct Vnode *vnode = NULL;
151 struct Vnode *parentVnode = NULL;
152 char *fullpath = NULL;
153
154 VnodeHold();
155 ret = follow_symlink(dirfd, path, &vnode, &fullpath);
156 if (ret == OK)
157 {
158 /* if file exist */
159 if (vnode->type == VNODE_TYPE_BCHR)
160 {
161 ret = -EINVAL;
162 VnodeDrop();
163 goto errout;
164 }
165 #ifdef LOSCFG_KERNEL_DEV_PLIMIT
166 if (vnode->type == VNODE_TYPE_CHR)
167 {
168 if (OsDevLimitCheckPermission(vnode->type, fullpath, oflags) != LOS_OK)
169 {
170 ret = -EPERM;
171 VnodeDrop();
172 goto errout;
173 }
174 }
175 #endif
176 #ifdef LOSCFG_FS_VFS_BLOCK_DEVICE
177 if (vnode->type == VNODE_TYPE_BLK)
178 {
179 VnodeDrop();
180 int fd = block_proxy(fullpath, oflags);
181 if (fd < 0)
182 {
183 ret = fd;
184 goto errout;
185 }
186 #ifdef LOSCFG_KERNEL_DEV_PLIMIT
187 if (OsDevLimitCheckPermission(vnode->type, fullpath, oflags) != LOS_OK)
188 {
189 ret = -EPERM;
190 goto errout;
191 }
192 #endif
193 return fd;
194 }
195 #endif
196 if ((vnode->originMount) && (vnode->originMount->mountFlags & MS_RDONLY) &&
197 (((oflags & O_ACCMODE) != O_RDONLY) || (oflags & O_TRUNC)))
198 {
199 ret = -EROFS;
200 VnodeDrop();
201 goto errout;
202 }
203 if ((oflags & O_CREAT) && (oflags & O_EXCL))
204 {
205 ret = -EEXIST;
206 VnodeDrop();
207 goto errout;
208 }
209 if (vnode->type == VNODE_TYPE_DIR)
210 {
211 ret = -EISDIR;
212 VnodeDrop();
213 goto errout;
214 }
215 accmode = oflag_convert_mode(oflags);
216 if (VfsVnodePermissionCheck(vnode, accmode))
217 {
218 ret = -EACCES;
219 VnodeDrop();
220 goto errout;
221 }
222 }
223
224 if ((ret != OK) && (oflags & O_CREAT) && vnode)
225 {
226 /* if file not exist, but parent dir of the file is exist */
227 if ((vnode->originMount) && (vnode->originMount->mountFlags & MS_RDONLY))
228 {
229 ret = -EROFS;
230 VnodeDrop();
231 goto errout;
232 }
233 if (VfsVnodePermissionCheck(vnode, (WRITE_OP | EXEC_OP)))
234 {
235 ret = -EACCES;
236 VnodeDrop();
237 goto errout;
238 }
239 parentVnode = vnode;
240 ret = do_creat(parentVnode, fullpath, mode, &vnode);
241 if (ret != OK)
242 {
243 VnodeDrop();
244 goto errout;
245 }
246 vnode->filePath = strdup(fullpath);
247 }
248
249 if (ret != OK)
250 {
251 /* found nothing */
252 VnodeDrop();
253 goto errout;
254 }
255 vnode->useCount++;
256 VnodeDrop();
257
258 if (oflags & O_TRUNC)
259 {
260 if (vnode->useCount > 1)
261 {
262 ret = -EBUSY;
263 goto errout_with_count;
264 }
265
266 if (vnode->vop->Truncate)
267 {
268 ret = vnode->vop->Truncate(vnode, 0);
269 if (ret != OK)
270 {
271 goto errout_with_count;
272 }
273 }
274 else
275 {
276 ret = -ENOSYS;
277 goto errout_with_count;
278 }
279 }
280
281 filep = files_allocate(vnode, oflags, 0, NULL, FILE_START_FD);
282 if (filep == NULL)
283 {
284 ret = -EMFILE;
285 goto errout_with_count;
286 }
287
288 if (filep->ops && filep->ops->open)
289 {
290 ret = filep->ops->open(filep);
291 }
292
293 if (ret < 0)
294 {
295 files_release(filep->fd);
296 goto errout_with_count;
297 }
298
299 if (fullpath)
300 {
301 free(fullpath);
302 }
303 return filep->fd;
304
305 errout_with_count:
306 VnodeHold();
307 vnode->useCount--;
308 VnodeDrop();
309 errout:
310 if (fullpath)
311 {
312 free(fullpath);
313 }
314 set_errno(-ret);
315 return VFS_ERROR;
316 }
317
do_open(int dirfd,const char * path,int oflags,mode_t mode)318 int do_open(int dirfd, const char *path, int oflags, mode_t mode)
319 {
320 int ret;
321 int fd;
322
323 if ((oflags & (O_WRONLY | O_CREAT)) != 0)
324 {
325 mode &= ~GetUmask();
326 mode &= (S_IRWXU | S_IRWXG | S_IRWXO);
327 }
328
329 fd = fp_open(dirfd, path, oflags, mode);
330 if (fd < 0)
331 {
332 ret = -get_errno();
333 goto errout;
334 }
335
336 return fd;
337
338 errout:
339 set_errno(-ret);
340 return VFS_ERROR;
341 }
342
343 /****************************************************************************
344 * Name: open
345 *
346 * Description: Standard 'open' interface
347 *
348 ****************************************************************************/
349
open(const char * path,int oflags,...)350 int open(const char *path, int oflags, ...)
351 {
352 mode_t mode = DEFAULT_FILE_MODE; /* File read-write properties. */
353 #ifdef LOSCFG_FILE_MODE
354 va_list ap;
355 va_start(ap, oflags);
356 mode = va_arg(ap, int);
357 va_end(ap);
358 #endif
359
360 return do_open(AT_FDCWD, path, oflags, mode);
361 }
362
open64(const char * __path,int __oflag,...)363 int open64 (const char *__path, int __oflag, ...)
364 {
365 mode_t mode = DEFAULT_FILE_MODE; /* File read-write properties. */
366 #ifdef LOSCFG_FILE_MODE
367 va_list ap;
368 va_start(ap, __oflag);
369 mode = va_arg(ap, int);
370 va_end(ap);
371 if ((__oflag & (O_WRONLY | O_CREAT)) != 0)
372 {
373 mode &= ~GetUmask();
374 mode &= (S_IRWXU|S_IRWXG|S_IRWXO);
375 }
376 #endif
377 return open (__path, ((unsigned int)__oflag) | O_LARGEFILE, mode);
378 }
379