summaryrefslogtreecommitdiff
path: root/components/lwp/syscall/syscall_file_path.c
blob: bf086098a33948c530be27dc38597cd02c25ffbe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
/*
 * Copyright (c) 2006-2025 RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include "syscall_internal.h"

/**
 * @brief Gets the current working directory.
 *
 * This function retrieves the absolute pathname of the current working directory
 * and stores it in the provided buffer. The buffer must be large enough to hold
 * the directory path, including the null-terminator. If the buffer is too small,
 * the function will return an error.
 *
 * @param[out] buf     A pointer to a buffer where the current working directory
 *                     path will be stored. The buffer should be large enough
 *                     to hold the path, including the null-terminator.
 * @param[in] size     The size of the buffer in bytes. The buffer must be large enough
 *                     to accommodate the full path.
 *
 * @return long        On success, returns the number of bytes written to the buffer
 *                     (not including the null-terminator). If the buffer is not large
 *                     enough, returns a negative error code.
 *
 * @note The maximum path length depends on the system's configuration.
 *       Ensure the buffer size is sufficient to hold the entire path.
 *
 * @see sys_chdir(), sys_getcwd_r(), sys_realpath()
 */
long sys_getcwd(char *buf, size_t size)
{
    char *tmp = RT_NULL;
    long ret = -1;

    if (!lwp_user_accessable((void *)buf, size))
    {
        return ret;
    }

    tmp = (char *)rt_malloc(size);
    if (!tmp)
    {
        return ret;
    }

    if (getcwd(tmp, size) != RT_NULL)
    {
        if (lwp_put_to_user(buf, tmp, size) > 0)
        {
            if (buf != RT_NULL)
            {
                ret = strlen(buf);
            }
            else
            {
                ret = -EFAULT;
            }
        }
    }

    rt_free(tmp);

    return ret;
}

/**
 * @brief Changes the current working directory.
 *
 * This function changes the current working directory of the calling process
 * to the directory specified by the given path. The path can be absolute or
 * relative. If the specified path does not exist or the process does not have
 * sufficient permissions, the function will return an error.
 *
 * @param[in] path     The path to the new working directory. This can be either
 *                     an absolute or a relative path.
 *
 * @return sysret_t    Returns `0` on success, indicating that the working
 *                     directory was successfully changed. On failure, returns a
 *                     negative error code indicating the reason for failure.
 *
 * @note If the specified path is a relative path, it is interpreted relative
 *       to the current working directory.
 *
 * @see sys_getcwd(), sys_chdir() for changing directories, sys_opendir(), sys_stat()
 */
sysret_t sys_chdir(const char *path)
{
#ifdef ARCH_MM_MMU
    int err = 0;
    int len = 0;
    int errcode;
    char *kpath = RT_NULL;

    len = lwp_user_strlen(path);
    if (len <= 0)
    {
        return -EFAULT;
    }

    kpath = (char *)kmem_get(len + 1);
    if (!kpath)
    {
        return -ENOMEM;
    }

    if (lwp_get_from_user(kpath, (void *)path, len + 1) != (len + 1))
    {
        kmem_put(kpath);
        return -EINVAL;
    }

    err = chdir(kpath);
    errcode = err != 0 ? GET_ERRNO() : 0;

    kmem_put(kpath);

    return errcode;
#else
    int ret = chdir(path);
    return (ret < 0 ? GET_ERRNO() : ret);
#endif
}

/**
 * @brief Changes the current working directory to the one associated with the specified file descriptor.
 *
 * This function changes the current working directory of the calling process
 * to the directory associated with the file descriptor `fd`. The file descriptor
 * should refer to an open directory. If the file descriptor does not refer to a
 * directory, or if the process lacks the necessary permissions, the function will
 * return an error.
 *
 * @param[in] fd   The file descriptor referring to the directory to which the current
 *                 working directory should be changed.
 *
 * @return sysret_t    Returns `0` on success, indicating the current working directory
 *                     was successfully changed. On failure, returns a negative error code.
 *
 * @note The file descriptor must refer to a directory. If it refers to a file or other
 *       non-directory object, the operation will fail.
 *
 * @see sys_chdir(), sys_getcwd(), sys_opendir(), sys_open()
 */
sysret_t sys_fchdir(int fd)
{
    int errcode = -ENOSYS;
#ifdef ARCH_MM_MMU
#ifdef RT_USING_DFS_V2
    int err = -1;
    struct dfs_file *d;
    char *kpath;

    d = fd_get(fd);
    if (!d || !d->vnode)
    {
        return -EBADF;
    }
    kpath = dfs_dentry_full_path(d->dentry);
    if (!kpath)
    {
        return -EACCES;
    }

    err = chdir(kpath);
    errcode = err != 0 ? GET_ERRNO() : 0;

    kmem_put(kpath);
#endif
#endif
    return errcode;
}

/**
 * @brief Creates a new directory with the specified path and mode.
 *
 * This function creates a new directory with the specified path and mode.
 * The directory is created with the permissions specified by the `mode` parameter.
 * If the directory already exists, the function will return an error.
 *
 * @param[in] path     The path of the directory to be created. This can be an absolute
 *                     or relative path. If the directory does not exist, it will be created.
 * @param[in] mode     The permissions to be set for the new directory. This parameter
 *                     specifies the access permissions for the directory owner, group, and others.
 *
 * @return sysret_t    Returns `0` on success, indicating that the directory was successfully
 *                     created. On failure, returns a negative error code indicating the reason for failure.
 *
 * @note The `mode` parameter specifies the permissions for the new directory. The permissions
 *       are typically specified using the `S_IRWXU`, `S_IRWXG`, and `S_IRWXO` macros, which
 *       define the read, write, and execute permissions for the owner, group, and others.
 *
 * @see sys_rmdir(), sys_chdir(), sys_mkdirat()
 */
sysret_t sys_mkdir(const char *path, mode_t mode)
{
#ifdef ARCH_MM_MMU
    int err = 0;
    int len = 0;
    char *kpath = RT_NULL;

    len = lwp_user_strlen(path);
    if (len <= 0)
    {
        return -EFAULT;
    }

    kpath = (char *)kmem_get(len + 1);
    if (!kpath)
    {
        return -ENOMEM;
    }

    if (lwp_get_from_user(kpath, (void *)path, len + 1) != (len + 1))
    {
        kmem_put(kpath);
        return -EINVAL;
    }

    err = _SYS_WRAP(mkdir(kpath, mode));

    kmem_put(kpath);

    return err;
#else
    int ret = mkdir(path, mode);
    return (ret < 0 ? GET_ERRNO() : ret);
#endif
}

/**
 * @brief Removes the specified directory.
 *
 * This function removes the directory specified by the given path. The directory
 * must be empty for the operation to succeed. If the directory is not empty, the
 * function will return an error. If the directory does not exist or the process
 * lacks the necessary permissions, the function will also return an error.
 *
 * @param[in] path     The path of the directory to be removed. This can be an absolute
 *                     or relative path. The directory must be empty for the operation to succeed.
 *
 * @return sysret_t    Returns `0` on success, indicating that the directory was successfully
 *                     removed. On failure, returns a negative error code indicating the reason for failure.
 *
 * @note The directory must be empty for the operation to succeed. If the directory contains
 *       files or subdirectories, the operation will fail. To remove a non-empty directory,
 *       the contents must be deleted first.
 *
 * @see sys_mkdir(), sys_chdir(), sys_rmdirat()
 */
sysret_t sys_rmdir(const char *path)
{
    int err = 0;
    int ret = 0;
#ifdef ARCH_MM_MMU
    int len = 0;
    char *kpath = RT_NULL;

    len = lwp_user_strlen(path);
    if (len <= 0)
    {
        return -EFAULT;
    }

    kpath = (char *)kmem_get(len + 1);
    if (!kpath)
    {
        return -ENOMEM;
    }

    if (lwp_get_from_user(kpath, (void *)path, len + 1) != (len + 1))
    {
        kmem_put(kpath);
        return -EINVAL;
    }

    ret = rmdir(kpath);
    if (ret < 0)
    {
        err = GET_ERRNO();
    }

    kmem_put(kpath);

    return (err < 0 ? err : ret);
#else
    ret = rmdir(path);
    if (ret < 0)
    {
        err = GET_ERRNO();
    }
    return (err < 0 ? err : ret);
#endif
}

/**
 * @brief Reads directory entries.
 *
 * This function reads the directory entries from the directory stream specified by the file descriptor `fd`.
 * It stores the directory entries in the buffer pointed to by `dirp`, up to the specified number of bytes (`nbytes`).
 * The entries are returned in a format compatible with the `struct libc_dirent` structure.
 *
 * @param[in] fd      The file descriptor referring to an open directory. This descriptor must be obtained
 *                    from a call to `sys_open()` with the appropriate flags for directory reading.
 * @param[out] dirp   A pointer to a buffer where the directory entries will be stored.
 *                    The buffer must be large enough to hold at least `nbytes` bytes of directory entries.
 * @param[in] nbytes  The size of the buffer (`dirp`) in bytes. It limits the number of directory entries
 *                    that can be read in a single call. The function will return as many entries as possible
 *                    that fit within the buffer size.
 *
 * @return sysret_t   Returns the number of bytes read on success, which may be less than `nbytes` if there
 *                     are fewer entries in the directory or if the buffer is too small.
 *                     On failure, returns a negative error code.
 *
 * @note If the function returns 0, it indicates the end of the directory stream. A negative return value
 *       indicates an error. The caller can use the returned number of bytes to process the entries in `dirp`.
 *
 * @see sys_open(), sys_close(), sys_readdir(), sys_stat()
 */
sysret_t sys_getdents(int fd, struct libc_dirent *dirp, size_t nbytes)
{
    int ret = -1;
    struct dfs_file *file;
    size_t cnt = (nbytes / sizeof(struct libc_dirent));
    size_t rtt_nbytes = 0;
    struct dirent *rtt_dirp;

#ifdef ARCH_MM_MMU
    if (!lwp_user_accessable((void *)dirp, sizeof(struct libc_dirent)))
    {
        return -EFAULT;
    }
#endif

    if (cnt == 0)
    {
        return -EINVAL;
    }
    rtt_nbytes = cnt * sizeof(struct dirent);
    rtt_dirp = (struct dirent *)rt_malloc(rtt_nbytes);
    if (!rtt_dirp)
    {
        return -ENOMEM;
    }
    file = fd_get(fd);
    ret = dfs_file_getdents(file, rtt_dirp, rtt_nbytes);
    if (ret > 0)
    {
        size_t i = 0;
        cnt = ret / sizeof(struct dirent);
        for (i = 0; i < cnt; i++)
        {
            dirp[i].d_ino = 0;
            dirp[i].d_off = i * sizeof(struct libc_dirent);
            dirp[i].d_type = rtt_dirp[i].d_type;
            dirp[i].d_reclen = sizeof(struct libc_dirent);
            strcpy(dirp[i].d_name, rtt_dirp[i].d_name);
        }
        ret = cnt * sizeof(struct libc_dirent);
    }

    if (ret < 0)
    {
        ret = GET_ERRNO();
    }

    rt_free(rtt_dirp);

    return ret;
}

/**
 * @brief Retrieves the current value of the error code.
 *
 * This function returns the most recent error code set by a system call. The error code is typically set
 * when a system call fails. This function allows users to retrieve the last error that occurred, helping to
 * diagnose issues or handle errors in a more controlled manner.
 *
 * @return sysret_t  The current error code. A value of `0` indicates no error, while a non-zero value
 *                   represents the most recent error. The error codes are system-specific and can
 *                   represent various failure conditions (e.g., `EINVAL`, `ENOMEM`, `EIO`).
 *
 * @note The error code returned is specific to the current thread or process and is typically updated
 *       each time a system call fails. The error code is persistent until it is overwritten by the next
 *       failed system call or explicitly reset.
 *
 * @see sys_set_errno(), sys_perror(), sys_strerror()
 */
sysret_t sys_access(const char *filename, int mode)
{
    int ret = 0;
#ifdef ARCH_MM_MMU
    rt_size_t len = 0;
    char *kfilename = RT_NULL;

    len = lwp_user_strlen(filename);
    if (len <= 0)
    {
        return -EINVAL;
    }

    kfilename = (char *)kmem_get(len + 1);
    if (!kfilename)
    {
        return -ENOMEM;
    }

    if (lwp_get_from_user(kfilename, (void *)filename, len + 1) != (len + 1))
    {
        kmem_put(kfilename);
        return -EFAULT;
    }

    ret = access(kfilename, mode);
    if (ret < 0)
    {
        ret = GET_ERRNO();
    }

    kmem_put(kfilename);
    return ret;
#else
    ret = access(filename, mode);
    return (ret < 0 ? GET_ERRNO() : ret);
#endif
}

/**
 * @brief Creates a pipe, a unidirectional data channel.
 *
 * This function creates a pipe, which is a unidirectional data channel used for inter-process communication.
 * The pipe consists of two file descriptors: one for reading from the pipe and one for writing to the pipe.
 * The pipe is used for passing data between processes or threads, typically in a producer-consumer scenario.
 *
 * @param[out] fd  An array of two integers where the file descriptors for the read and write ends of the pipe will be stored.
 *                 - `fd[0]`: The file descriptor for reading from the pipe.
 *                 - `fd[1]`: The file descriptor for writing to the pipe.
 *
 * @return sysret_t   Returns `0` on success. On failure, returns a negative error code.
 *
 * @note The pipe created by this function is typically used for simple communication between processes or threads.
 *       The data written to `fd[1]` can be read from `fd[0]`. After usage, both file descriptors should be closed.
 *
 * @see sys_read(), sys_write(), sys_close(), sys_fork(), sys_execve()
 */
sysret_t sys_pipe(int fd[2])
{
    int ret;
    int kfd[2] = { 0, 0 };

    if (!lwp_user_accessable((void *)fd, sizeof(int[2])))
    {
        return -EFAULT;
    }

    ret = pipe(kfd);

    lwp_put_to_user((void *)fd, kfd, sizeof(int[2]));

    return (ret < 0 ? GET_ERRNO() : ret);
}