1/*
2 * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
3 * Copyright (c) 2020-2021 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 <securec.h>
33#include "stdio.h"
34#include "stdlib.h"
35#include "string.h"
36#include "errno.h"
37#include "limits.h"
38#include "shell.h"
39#include "show.h"
40
41#define TEMP_PATH_MAX (PATH_MAX + SHOW_MAX_LEN)
42
43STATIC UINT32 VfsStrnlen(const CHAR *str, size_t maxlen)
44{
45    const CHAR *p = NULL;
46
47    for (p = str; ((maxlen-- != 0) && (*p != '\0')); ++p) {}
48
49    return p - str;
50}
51
52/* abandon the redundant '/' in the path, only keep one. */
53STATIC CHAR *StrPath(CHAR *path)
54{
55    CHAR *dest = path;
56    CHAR *src = path;
57
58    while (*src != '\0') {
59        if (*src == '/') {
60            *dest++ = *src++;
61            while (*src == '/') {
62                src++;
63            }
64            continue;
65        }
66        *dest++ = *src++;
67    }
68    *dest = '\0';
69    return path;
70}
71
72STATIC VOID StrRemovePathEndSlash(CHAR *dest, const CHAR *fullpath)
73{
74    if ((*dest == '.') && (*(dest - 1) == '/')) {
75        *dest = '\0';
76        dest--;
77    }
78    if ((dest != fullpath) && (*dest == '/')) {
79        *dest = '\0';
80    }
81}
82
83STATIC CHAR *StrNormalizePath(CHAR *fullpath)
84{
85    CHAR *dest = fullpath;
86    CHAR *src = fullpath;
87
88    /* 2: The position of the path character: / and the end character /0 */
89    while (*src != '\0') {
90        if (*src == '.') {
91            if (*(src + 1) == '/') {
92                src += 2; /* 2, sizeof "./" */
93                continue;
94            } else if (*(src + 1) == '.') {
95                if ((*(src + 2) == '/') || (*(src + 2) == '\0')) { /* 2, 2, offset to check */
96                    src += 2; /* 2, sizeof offset */
97                } else {
98                    while ((*src != '\0') && (*src != '/')) {
99                        *dest++ = *src++;
100                    }
101                    continue;
102                }
103            } else {
104                *dest++ = *src++;
105                continue;
106            }
107        } else {
108            *dest++ = *src++;
109            continue;
110        }
111
112        if ((dest - 1) != fullpath) {
113            dest--;
114        }
115
116        while ((dest > fullpath) && (*(dest - 1) != '/')) {
117            dest--;
118        }
119
120        if (*src == '/') {
121            src++;
122        }
123    }
124
125    *dest = '\0';
126
127    /* remove '/' in the end of path if exist */
128
129    dest--;
130
131    StrRemovePathEndSlash(dest, fullpath);
132    return dest;
133}
134
135STATIC INT32 VfsNormalizePathParameCheck(const CHAR *filename, CHAR **pathname)
136{
137    INT32 namelen;
138    CHAR *name = NULL;
139
140    if (pathname == NULL) {
141        return -EINVAL;
142    }
143
144    /* check parameters */
145
146    if (filename == NULL) {
147        *pathname = NULL;
148        return -EINVAL;
149    }
150
151    namelen = VfsStrnlen(filename, PATH_MAX);
152    if (!namelen) {
153        *pathname = NULL;
154        return -EINVAL;
155    } else if (namelen >= PATH_MAX) {
156        *pathname = NULL;
157        return -ENAMETOOLONG;
158    }
159
160    for (name = (CHAR *)filename + namelen; ((name != filename) && (*name != '/')); name--) {
161        if (strlen(name) > NAME_MAX) {
162            *pathname = NULL;
163            return -ENAMETOOLONG;
164        }
165    }
166
167    return namelen;
168}
169
170STATIC CHAR *VfsNotAbsolutePath(const CHAR *directory, const CHAR *filename, CHAR **pathname, INT32 namelen)
171{
172    INT32 ret;
173    CHAR *fullpath = NULL;
174
175    /* 2: The position of the path character: / and the end character /0 */
176
177    if ((namelen > 1) && (filename[0] == '.') && (filename[1] == '/')) {
178        filename += 2; /* 2, size of "./" */
179    }
180
181    fullpath = (CHAR *)malloc(strlen(directory) + namelen + 2); /* 2, size of "./" */
182    if (fullpath == NULL) {
183        *pathname = NULL;
184        SetErrno(ENOMEM);
185        return (CHAR *)NULL;
186    }
187
188    /* 2, size of "./", join path and file name */
189    ret = snprintf_s(fullpath, strlen(directory) + namelen + 2, strlen(directory) + namelen + 1,
190                     "%s/%s", directory, filename);
191    if (ret < 0) {
192        *pathname = NULL;
193        free(fullpath);
194        SetErrno(ENAMETOOLONG);
195        return (CHAR *)NULL;
196    }
197
198    return fullpath;
199}
200
201STATIC CHAR *VfsNormalizeFullpath(const CHAR *directory, const CHAR *filename, CHAR **pathname, INT32 namelen)
202{
203    CHAR *fullpath = NULL;
204
205    if (filename[0] != '/') {
206        /* not an absolute path */
207
208        fullpath = VfsNotAbsolutePath(directory, filename, pathname, namelen);
209        if (fullpath == NULL) {
210            return (CHAR *)NULL;
211        }
212    } else {
213        /* it's an absolute path, use it directly */
214
215        fullpath = strdup(filename); /* copy string */
216        if (fullpath == NULL) {
217            *pathname = NULL;
218            SetErrno(ENOMEM);
219            return (CHAR *)NULL;
220        }
221        if (filename[1] == '/') {
222            *pathname = NULL;
223            free(fullpath);
224            SetErrno(EINVAL);
225            return (CHAR *)NULL;
226        }
227    }
228
229    return fullpath;
230}
231
232INT32 VfsNormalizePath(const CHAR *directory, const CHAR *filename, CHAR **pathname)
233{
234    CHAR *fullpath = NULL;
235    INT32 namelen;
236
237    namelen = VfsNormalizePathParameCheck(filename, pathname);
238    if (namelen < 0) {
239        return namelen;
240    }
241
242    if ((directory == NULL) && (filename[0] != '/')) {
243        PRINT_ERR("NO_WORKING_DIR\n");
244        *pathname = NULL;
245        return -EINVAL;
246    }
247
248    /* 2, The position of the path character: / and the end character /0 */
249    if ((filename[0] != '/') && (strlen(directory) + namelen + 2 > TEMP_PATH_MAX)) {
250        return -ENAMETOOLONG;
251    }
252
253    fullpath = VfsNormalizeFullpath(directory, filename, pathname, namelen);
254    if (fullpath == NULL) {
255        return -errno;
256    }
257
258    (VOID)StrPath(fullpath);
259    (VOID)StrNormalizePath(fullpath);
260    if (strlen(fullpath) >= PATH_MAX) {
261        *pathname = NULL;
262        free(fullpath);
263        return -ENAMETOOLONG;
264    }
265
266    *pathname = fullpath;
267    return 0;
268}
269
270