]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/Reliance-Edge/posix/path.c
Update version numbers in preparation for new release.
[freertos] / FreeRTOS-Plus / Source / Reliance-Edge / posix / path.c
1 /*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----\r
2 \r
3                    Copyright (c) 2014-2015 Datalight, Inc.\r
4                        All Rights Reserved Worldwide.\r
5 \r
6     This program is free software; you can redistribute it and/or modify\r
7     it under the terms of the GNU General Public License as published by\r
8     the Free Software Foundation; use version 2 of the License.\r
9 \r
10     This program is distributed in the hope that it will be useful,\r
11     but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty\r
12     of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13     GNU General Public License for more details.\r
14 \r
15     You should have received a copy of the GNU General Public License along\r
16     with this program; if not, write to the Free Software Foundation, Inc.,\r
17     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
18 */\r
19 /*  Businesses and individuals that for commercial or other reasons cannot\r
20     comply with the terms of the GPLv2 license may obtain a commercial license\r
21     before incorporating Reliance Edge into proprietary software for\r
22     distribution in any form.  Visit http://www.datalight.com/reliance-edge for\r
23     more information.\r
24 */\r
25 /** @file\r
26     @brief Implements path utilities for the POSIX-like API layer.\r
27 */\r
28 #include <redfs.h>\r
29 \r
30 #if REDCONF_API_POSIX == 1\r
31 \r
32 #include <redcoreapi.h>\r
33 #include <redvolume.h>\r
34 #include <redposix.h>\r
35 #include "redpath.h"\r
36 \r
37 \r
38 static bool IsRootDir(const char *pszLocalPath);\r
39 static bool PathHasMoreNames(const char *pszPathIdx);\r
40 \r
41 \r
42 /** @brief Split a path into its component parts: a volume and a volume-local\r
43            path.\r
44 \r
45     @param pszPath          The path to split.\r
46     @param pbVolNum         On successful return, if non-NULL, populated with\r
47                             the volume number extracted from the path.\r
48     @param ppszLocalPath    On successful return, populated with the\r
49                             volume-local path: the path stripped of any volume\r
50                             path prefixing.  If this parameter is NULL, that\r
51                             indicates there should be no local path, and any\r
52                             characters beyond the prefix (other than path\r
53                             separators) are treated as an error.\r
54 \r
55     @return A negated ::REDSTATUS code indicating the operation result.\r
56 \r
57     @retval 0           Operation was successful.\r
58     @retval -RED_EINVAL @p pszPath is `NULL`.\r
59     @retval -RED_ENOENT @p pszPath could not be matched to any volume; or\r
60                         @p ppszLocalPath is NULL but @p pszPath includes a local\r
61                         path.\r
62 */\r
63 REDSTATUS RedPathSplit(\r
64     const char     *pszPath,\r
65     uint8_t        *pbVolNum,\r
66     const char    **ppszLocalPath)\r
67 {\r
68     REDSTATUS       ret = 0;\r
69 \r
70     if(pszPath == NULL)\r
71     {\r
72         ret = -RED_EINVAL;\r
73     }\r
74     else\r
75     {\r
76         const char *pszLocalPath = pszPath;\r
77         uint8_t     bMatchVol = UINT8_MAX;\r
78         uint32_t    ulMatchLen = 0U;\r
79         uint8_t     bDefaultVolNum = UINT8_MAX;\r
80         uint8_t     bVolNum;\r
81 \r
82         for(bVolNum = 0U; bVolNum < REDCONF_VOLUME_COUNT; bVolNum++)\r
83         {\r
84             const char *pszPrefix = gaRedVolConf[bVolNum].pszPathPrefix;\r
85             uint32_t    ulPrefixLen = RedStrLen(pszPrefix);\r
86 \r
87             if(ulPrefixLen == 0U)\r
88             {\r
89                 /*  A volume with a path prefix of an empty string is the\r
90                     default volume, used when the path does not match the\r
91                     prefix of any other volume.\r
92 \r
93                     The default volume should only be found once.  During\r
94                     initialization, RedCoreInit() ensures that all volume\r
95                     prefixes are unique (including empty prefixes).\r
96                 */\r
97                 REDASSERT(bDefaultVolNum == UINT8_MAX);\r
98                 bDefaultVolNum = bVolNum;\r
99             }\r
100             /*  For a path to match, it must either be the prefix exactly, or\r
101                 be followed by a path separator character.  Thus, with a volume\r
102                 prefix of "/foo", both "/foo" and "/foo/bar" are matches, but\r
103                 "/foobar" is not.\r
104             */\r
105             else if(    (RedStrNCmp(pszPath, pszPrefix, ulPrefixLen) == 0)\r
106                      && ((pszPath[ulPrefixLen] == '\0') || (pszPath[ulPrefixLen] == REDCONF_PATH_SEPARATOR)))\r
107             {\r
108                 /*  The length of this match should never exactly equal the\r
109                     length of a previous match: that would require a duplicate\r
110                     volume name, which should have been detected during init.\r
111                 */\r
112                 REDASSERT(ulPrefixLen != ulMatchLen);\r
113 \r
114                 /*  If multiple prefixes match, the longest takes precedence.\r
115                     Thus, if there are two prefixes "Flash" and "Flash/Backup",\r
116                     the path "Flash/Backup/" will not be erroneously matched\r
117                     with the "Flash" volume.\r
118                 */\r
119                 if(ulPrefixLen > ulMatchLen)\r
120                 {\r
121                     bMatchVol = bVolNum;\r
122                     ulMatchLen = ulPrefixLen;\r
123                 }\r
124             }\r
125             else\r
126             {\r
127                 /*  No match, keep looking.\r
128                 */\r
129             }\r
130         }\r
131 \r
132         if(bMatchVol != UINT8_MAX)\r
133         {\r
134             /*  The path matched a volume path prefix.\r
135             */\r
136             bVolNum = bMatchVol;\r
137             pszLocalPath = &pszPath[ulMatchLen];\r
138         }\r
139         else if(bDefaultVolNum != UINT8_MAX)\r
140         {\r
141             /*  The path didn't match any of the prefixes, but one of the\r
142                 volumes has a path prefix of "", so an unprefixed path is\r
143                 assigned to that volume.\r
144             */\r
145             bVolNum = bDefaultVolNum;\r
146             REDASSERT(pszLocalPath == pszPath);\r
147         }\r
148         else\r
149         {\r
150             /*  The path cannot be assigned a volume.\r
151             */\r
152             ret = -RED_ENOENT;\r
153         }\r
154 \r
155         if(ret == 0)\r
156         {\r
157             if(pbVolNum != NULL)\r
158             {\r
159                 *pbVolNum = bVolNum;\r
160             }\r
161 \r
162             if(ppszLocalPath != NULL)\r
163             {\r
164                 *ppszLocalPath = pszLocalPath;\r
165             }\r
166             else\r
167             {\r
168                 /*  If no local path is expected, then the string should either\r
169                     terminate after the path prefix or the local path should name\r
170                     the root directory.  Allowing path separators here means that\r
171                     red_mount("/data/") is OK with a path prefix of "/data".\r
172                 */\r
173                 if(pszLocalPath[0U] != '\0')\r
174                 {\r
175                     if(!IsRootDir(pszLocalPath))\r
176                     {\r
177                         ret = -RED_ENOENT;\r
178                     }\r
179                 }\r
180             }\r
181         }\r
182     }\r
183 \r
184     return ret;\r
185 }\r
186 \r
187 \r
188 /** @brief Lookup the inode named by the given path.\r
189 \r
190     @param pszLocalPath The path to lookup; this is a local path, without any\r
191                         volume prefix.\r
192     @param pulInode     On successful return, populated with the number of the\r
193                         inode named by @p pszLocalPath.\r
194 \r
195     @return A negated ::REDSTATUS code indicating the operation result.\r
196 \r
197     @retval 0                   Operation was successful.\r
198     @retval -RED_EINVAL         @p pszLocalPath is `NULL`; or @p pulInode is\r
199                                 `NULL`.\r
200     @retval -RED_EIO            A disk I/O error occurred.\r
201     @retval -RED_ENOENT         @p pszLocalPath is an empty string; or\r
202                                 @p pszLocalPath does not name an existing file\r
203                                 or directory.\r
204     @retval -RED_ENOTDIR        A component of the path other than the last is\r
205                                 not a directory.\r
206     @retval -RED_ENAMETOOLONG   The length of a component of @p pszLocalPath is\r
207                                 longer than #REDCONF_NAME_MAX.\r
208 */\r
209 REDSTATUS RedPathLookup(\r
210     const char *pszLocalPath,\r
211     uint32_t   *pulInode)\r
212 {\r
213     REDSTATUS   ret;\r
214 \r
215     if((pszLocalPath == NULL) || (pulInode == NULL))\r
216     {\r
217         REDERROR();\r
218         ret = -RED_EINVAL;\r
219     }\r
220     else if(pszLocalPath[0U] == '\0')\r
221     {\r
222         ret = -RED_ENOENT;\r
223     }\r
224     else if(IsRootDir(pszLocalPath))\r
225     {\r
226         ret = 0;\r
227         *pulInode = INODE_ROOTDIR;\r
228     }\r
229     else\r
230     {\r
231         uint32_t    ulPInode;\r
232         const char *pszName;\r
233 \r
234         ret = RedPathToName(pszLocalPath, &ulPInode, &pszName);\r
235 \r
236         if(ret == 0)\r
237         {\r
238             ret = RedCoreLookup(ulPInode, pszName, pulInode);\r
239         }\r
240     }\r
241 \r
242     return ret;\r
243 }\r
244 \r
245 \r
246 /** @brief Given a path, return the parent inode number and a pointer to the\r
247            last component in the path (the name).\r
248 \r
249     @param pszLocalPath The path to examine; this is a local path, without any\r
250                         volume prefix.\r
251     @param pulPInode    On successful return, populated with the inode number of\r
252                         the parent directory of the last component in the path.\r
253                         For example, with the path "a/b/c", populated with the\r
254                         inode number of "b".\r
255     @param ppszName     On successful return, populated with a pointer to the\r
256                         last component in the path.  For example, with the path\r
257                         "a/b/c", populated with a pointer to "c".\r
258 \r
259     @return A negated ::REDSTATUS code indicating the operation result.\r
260 \r
261     @retval 0                   Operation was successful.\r
262     @retval -RED_EINVAL         @p pszLocalPath is `NULL`; or @p pulPInode is\r
263                                 `NULL`; or @p ppszName is `NULL`; or the path\r
264                                 names the root directory.\r
265     @retval -RED_EIO            A disk I/O error occurred.\r
266     @retval -RED_ENOENT         @p pszLocalPath is an empty string; or a\r
267                                 component of the path other than the last does\r
268                                 not exist.\r
269     @retval -RED_ENOTDIR        A component of the path other than the last is\r
270                                 not a directory.\r
271     @retval -RED_ENAMETOOLONG   The length of a component of @p pszLocalPath is\r
272                                 longer than #REDCONF_NAME_MAX.\r
273 */\r
274 REDSTATUS RedPathToName(\r
275     const char     *pszLocalPath,\r
276     uint32_t       *pulPInode,\r
277     const char    **ppszName)\r
278 {\r
279     REDSTATUS       ret;\r
280 \r
281     if((pszLocalPath == NULL) || (pulPInode == NULL) || (ppszName == NULL))\r
282     {\r
283         REDERROR();\r
284         ret = -RED_EINVAL;\r
285     }\r
286     else if(IsRootDir(pszLocalPath))\r
287     {\r
288         ret = -RED_EINVAL;\r
289     }\r
290     else if(pszLocalPath[0U] == '\0')\r
291     {\r
292         ret = -RED_ENOENT;\r
293     }\r
294     else\r
295     {\r
296         uint32_t ulInode = INODE_ROOTDIR;\r
297         uint32_t ulPInode = INODE_INVALID;\r
298         uint32_t ulPathIdx = 0U;\r
299         uint32_t ulLastNameIdx = 0U;\r
300 \r
301         ret = 0;\r
302 \r
303         do\r
304         {\r
305             uint32_t ulNameLen;\r
306 \r
307             /*  Skip over path separators, to get pszLocalPath[ulPathIdx]\r
308                 pointing at the next name.\r
309             */\r
310             while(pszLocalPath[ulPathIdx] == REDCONF_PATH_SEPARATOR)\r
311             {\r
312                 ulPathIdx++;\r
313             }\r
314 \r
315             if(pszLocalPath[ulPathIdx] == '\0')\r
316             {\r
317                 break;\r
318             }\r
319 \r
320             /*  Point ulLastNameIdx at the first character of the name; after\r
321                 we exit the loop, it will point at the first character of the\r
322                 last name in the path.\r
323             */\r
324             ulLastNameIdx = ulPathIdx;\r
325 \r
326             /*  Point ulPInode at the parent inode: either the root inode\r
327                 (first pass) or the inode of the previous name.  After we exit\r
328                 the loop, this will point at the parent inode of the last name.\r
329             */\r
330             ulPInode = ulInode;\r
331 \r
332             ulNameLen = RedNameLen(&pszLocalPath[ulPathIdx]);\r
333 \r
334             /*  Lookup the inode of the name, unless we are at the last name in\r
335                 the path: we don't care whether the last name exists or not.\r
336             */\r
337             if(PathHasMoreNames(&pszLocalPath[ulPathIdx + ulNameLen]))\r
338             {\r
339                 ret = RedCoreLookup(ulPInode, &pszLocalPath[ulPathIdx], &ulInode);\r
340             }\r
341 \r
342             /*  Move on to the next path element.\r
343             */\r
344             if(ret == 0)\r
345             {\r
346                 ulPathIdx += ulNameLen;\r
347             }\r
348         }\r
349         while(ret == 0);\r
350 \r
351         if(ret == 0)\r
352         {\r
353             *pulPInode = ulPInode;\r
354             *ppszName = &pszLocalPath[ulLastNameIdx];\r
355         }\r
356     }\r
357 \r
358     return ret;\r
359 }\r
360 \r
361 \r
362 /** @brief Determine whether a path names the root directory.\r
363 \r
364     @param pszLocalPath The path to examine; this is a local path, without any\r
365                         volume prefix.\r
366 \r
367     @return Returns whether @p pszLocalPath names the root directory.\r
368 \r
369     @retval true    @p pszLocalPath names the root directory.\r
370     @retval false   @p pszLocalPath does not name the root directory.\r
371 */\r
372 static bool IsRootDir(\r
373     const char *pszLocalPath)\r
374 {\r
375     bool        fRet;\r
376 \r
377     if(pszLocalPath == NULL)\r
378     {\r
379         REDERROR();\r
380         fRet = false;\r
381     }\r
382     else\r
383     {\r
384         uint32_t    ulIdx = 0U;\r
385 \r
386         /*  A string containing nothing but path separators (usually only one)\r
387             names the root directory.  An empty string does *not* name the root\r
388             directory, since in POSIX empty strings typically elicit -RED_ENOENT\r
389             errors.\r
390         */\r
391         while(pszLocalPath[ulIdx] == REDCONF_PATH_SEPARATOR)\r
392         {\r
393             ulIdx++;\r
394         }\r
395 \r
396         fRet = (ulIdx > 0U) && (pszLocalPath[ulIdx] == '\0');\r
397     }\r
398 \r
399     return fRet;\r
400 }\r
401 \r
402 \r
403 /** @brief Determine whether there are more names in a path.\r
404 \r
405     Example | Result\r
406     ------- | ------\r
407     ""        false\r
408     "\"       false\r
409     "\\"      false\r
410     "a"       true\r
411     "\a"      true\r
412     "\\a"     true\r
413 \r
414     @param pszPathIdx   The path to examine, incremented to the point of\r
415                         interest.\r
416 \r
417     @return Returns whether there are more names in @p pszPathIdx.\r
418 \r
419     @retval true    @p pszPathIdx has more names.\r
420     @retval false   @p pszPathIdx has no more names.\r
421 */\r
422 static bool PathHasMoreNames(\r
423     const char *pszPathIdx)\r
424 {\r
425     bool        fRet;\r
426 \r
427     if(pszPathIdx == NULL)\r
428     {\r
429         REDERROR();\r
430         fRet = false;\r
431     }\r
432     else\r
433     {\r
434         uint32_t ulIdx = 0U;\r
435 \r
436         while(pszPathIdx[ulIdx] == REDCONF_PATH_SEPARATOR)\r
437         {\r
438             ulIdx++;\r
439         }\r
440 \r
441         fRet = pszPathIdx[ulIdx] != '\0';\r
442     }\r
443 \r
444     return fRet;\r
445 }\r
446 \r
447 #endif /* REDCONF_API_POSIX */\r
448 \r