xref: /kernel/liteos_a/fs/vfs/operation/fullpath.c (revision 0d163575)
1/*
2 * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
3 * Copyright (c) 2020-2022 Huawei Device Co., Ltd. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this list of
9 *    conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
12 *    of conditions and the following disclaimer in the documentation and/or other materials
13 *    provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
16 *    to endorse or promote products derived from this software without specific prior written
17 *    permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include "stdio.h"
33#include "stdlib.h"
34#include "string.h"
35#include "errno.h"
36#include "limits.h"
37#include "los_process_pri.h"
38#include "fs/fd_table.h"
39#include "fs/file.h"
40
41#ifdef LOSCFG_SHELL
42#include "shell.h"
43#endif
44
45
46#ifdef LOSCFG_SHELL
47#define TEMP_PATH_MAX (PATH_MAX + SHOW_MAX_LEN)
48#else
49#define TEMP_PATH_MAX  PATH_MAX
50#endif
51
52static unsigned int vfs_strnlen(const char *str, size_t maxlen)
53{
54    const char *p = NULL;
55
56    for (p = str; ((maxlen-- != 0) && (*p != '\0')); ++p) {}
57
58    return p - str;
59}
60
61/* abandon the redundant '/' in the path, only keep one. */
62
63static char *str_path(char *path)
64{
65    char *dest = path;
66    char *src = path;
67
68    while (*src != '\0') {
69        if (*src == '/') {
70            *dest++ = *src++;
71            while (*src == '/') {
72                src++;
73            }
74            continue;
75        }
76        *dest++ = *src++;
77    }
78    *dest = '\0';
79    return path;
80}
81
82static void str_remove_path_end_slash(char *dest, const char *fullpath)
83{
84    if ((*dest == '.') && (*(dest - 1) == '/')) {
85        *dest = '\0';
86        dest--;
87    }
88    if ((dest != fullpath) && (*dest == '/')) {
89        *dest = '\0';
90    }
91}
92
93static char *str_normalize_path(char *fullpath)
94{
95    char *dest = fullpath;
96    char *src = fullpath;
97
98    /* 2: The position of the path character: / and the end character /0 */
99
100    while (*src != '\0') {
101        if (*src == '.') {
102            if (*(src + 1) == '/') {
103                src += 2;
104                continue;
105            } else if (*(src + 1) == '.') {
106                if ((*(src + 2) == '/') || (*(src + 2) == '\0')) {
107                    src += 2;
108                } else {
109                    while ((*src != '\0') && (*src != '/')) {
110                        *dest++ = *src++;
111                    }
112                    continue;
113                }
114            } else {
115                *dest++ = *src++;
116                continue;
117            }
118        } else {
119            *dest++ = *src++;
120            continue;
121        }
122
123        if ((dest - 1) != fullpath) {
124            dest--;
125        }
126
127        while ((dest > fullpath) && (*(dest - 1) != '/')) {
128            dest--;
129        }
130
131        if (*src == '/') {
132            src++;
133        }
134    }
135
136    *dest = '\0';
137
138    /* remove '/' in the end of path if exist */
139
140    dest--;
141
142    str_remove_path_end_slash(dest, fullpath);
143    return dest;
144}
145
146static int vfs_normalize_path_parame_check(const char *filename, char **pathname)
147{
148    int namelen;
149    char *name = NULL;
150
151    if (pathname == NULL) {
152        return -EINVAL;
153    }
154
155    /* check parameters */
156
157    if (filename == NULL) {
158        *pathname = NULL;
159        return -EINVAL;
160    }
161
162    namelen = vfs_strnlen(filename, PATH_MAX);
163    if (!namelen) {
164        *pathname = NULL;
165        return -EINVAL;
166    } else if (namelen >= PATH_MAX) {
167        *pathname = NULL;
168        return -ENAMETOOLONG;
169    }
170
171    for (name = (char *)filename + namelen; ((name != filename) && (*name != '/')); name--) {
172        if (strlen(name) > NAME_MAX) {
173            *pathname = NULL;
174            return -ENAMETOOLONG;
175        }
176    }
177
178    return namelen;
179}
180
181static char *vfs_not_absolute_path(const char *directory, const char *filename, char **pathname, int namelen)
182{
183    int ret;
184    char *fullpath = NULL;
185
186    /* 2: The position of the path character: / and the end character /0 */
187
188    if ((namelen > 1) && (filename[0] == '.') && (filename[1] == '/')) {
189        filename += 2;
190    }
191
192    fullpath = (char *)malloc(strlen(directory) + namelen + 2);
193    if (fullpath == NULL) {
194        *pathname = NULL;
195        set_errno(ENOMEM);
196        return (char *)NULL;
197    }
198
199    /* join path and file name */
200
201    ret = snprintf_s(fullpath, strlen(directory) + namelen + 2, strlen(directory) + namelen + 1,
202                     "%s/%s", directory, filename);
203    if (ret < 0) {
204        *pathname = NULL;
205        free(fullpath);
206        set_errno(ENAMETOOLONG);
207        return (char *)NULL;
208    }
209
210    return fullpath;
211}
212
213static char *vfs_normalize_fullpath(const char *directory, const char *filename, char **pathname, int namelen)
214{
215    char *fullpath = NULL;
216
217    if (filename[0] != '/') {
218        /* not a absolute path */
219
220        fullpath = vfs_not_absolute_path(directory, filename, pathname, namelen);
221        if (fullpath == NULL) {
222            return (char *)NULL;
223        }
224    } else {
225        /* it's a absolute path, use it directly */
226
227        fullpath = strdup(filename); /* copy string */
228        if (fullpath == NULL) {
229            *pathname = NULL;
230            set_errno(ENOMEM);
231            return (char *)NULL;
232        }
233        if (filename[1] == '/') {
234            *pathname = NULL;
235            free(fullpath);
236            set_errno(EINVAL);
237            return (char *)NULL;
238        }
239    }
240
241    return fullpath;
242}
243
244int vfs_normalize_path(const char *directory, const char *filename, char **pathname)
245{
246    char *fullpath = NULL;
247    int namelen;
248#ifdef VFS_USING_WORKDIR
249    UINTPTR lock_flags;
250    LosProcessCB *curr = OsCurrProcessGet();
251    BOOL dir_flags = (directory == NULL) ? TRUE : FALSE;
252#endif
253
254    namelen = vfs_normalize_path_parame_check(filename, pathname);
255    if (namelen < 0) {
256        return namelen;
257    }
258
259#ifdef VFS_USING_WORKDIR
260    if (directory == NULL) {
261        spin_lock_irqsave(&curr->files->workdir_lock, lock_flags);
262        directory = curr->files->workdir;
263    }
264#else
265    if ((directory == NULL) && (filename[0] != '/')) {
266        PRINT_ERR("NO_WORKING_DIR\n");
267        *pathname = NULL;
268        return -EINVAL;
269    }
270#endif
271
272    /* 2: The position of the path character: / and the end character /0 */
273
274    if ((filename[0] != '/') && (strlen(directory) + namelen + 2 > TEMP_PATH_MAX)) {
275#ifdef VFS_USING_WORKDIR
276        if (dir_flags == TRUE) {
277            spin_unlock_irqrestore(&curr->files->workdir_lock, lock_flags);
278        }
279#endif
280        return -ENAMETOOLONG;
281    }
282
283    fullpath = vfs_normalize_fullpath(directory, filename, pathname, namelen);
284#ifdef VFS_USING_WORKDIR
285    if (dir_flags == TRUE) {
286        spin_unlock_irqrestore(&curr->files->workdir_lock, lock_flags);
287    }
288#endif
289    if (fullpath == NULL) {
290        return -get_errno();
291    }
292
293    (void)str_path(fullpath);
294    (void)str_normalize_path(fullpath);
295    if (strlen(fullpath) >= PATH_MAX) {
296        *pathname = NULL;
297        free(fullpath);
298        return -ENAMETOOLONG;
299    }
300
301    *pathname = fullpath;
302    return ENOERR;
303}
304
305int vfs_normalize_pathat(int dirfd, const char *filename, char **pathname)
306{
307    /* Get path by dirfd */
308    char *relativeoldpath = NULL;
309    char *fullpath = NULL;
310    int ret = 0;
311
312    ret = get_path_from_fd(dirfd, &relativeoldpath);
313    if (ret < 0) {
314        return ret;
315    }
316
317    ret = vfs_normalize_path((const char *)relativeoldpath, filename, &fullpath);
318    if (relativeoldpath) {
319        free(relativeoldpath);
320    }
321
322    if (ret < 0) {
323        return ret;
324    }
325
326    *pathname = fullpath;
327    return ret;
328}
329
330