A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 by Björn Stenberg
11 * Copyright (C) 2014 by Michael Sevakis
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22#define DIRFUNCTIONS_DEFINED
23#include "config.h"
24#include <errno.h>
25#include <string.h>
26#include "debug.h"
27#include "dir.h"
28#include "pathfuncs.h"
29#include "timefuncs.h"
30#include "fileobj_mgr.h"
31#include "rb_namespace.h"
32
33/* structure used for open directory streams */
34static struct dirstr_desc
35{
36 struct filestr_base stream; /* basic stream info (first!) */
37 struct ns_scan_info scan; /* directory scan cursor */
38 struct dirent entry; /* current parsed entry information */
39} open_streams[MAX_OPEN_DIRS];
40
41/* check and return a struct dirstr_desc* from a DIR* */
42static struct dirstr_desc * get_dirstr(DIR *dirp)
43{
44 struct dirstr_desc *dir = (struct dirstr_desc *)dirp;
45
46 if (!PTR_IN_ARRAY(open_streams, dir, MAX_OPEN_DIRS))
47 dir = NULL;
48 else if (dir->stream.flags & (FDO_BUSY|FD_VALID))
49 return dir;
50
51 int errnum;
52
53 if (!dir)
54 {
55 errnum = EFAULT;
56 }
57 else if (dir->stream.flags & FD_NONEXIST)
58 {
59 DEBUGF("dir #%d: nonexistant device\n", (int)(dir - open_streams));
60 errnum = ENXIO;
61 }
62 else
63 {
64 DEBUGF("dir #%d: dir not open\n", (int)(dir - open_streams));
65 errnum = EBADF;
66 }
67
68 errno = errnum;
69 return NULL;
70}
71
72#define GET_DIRSTR(type, dirp) \
73 ({ \
74 file_internal_lock_##type(); \
75 struct dirstr_desc *_dir = get_dirstr(dirp); \
76 if (_dir) \
77 FILESTR_LOCK(type, &_dir->stream); \
78 else \
79 file_internal_unlock_##type(); \
80 _dir; \
81 })
82
83/* release the lock on the dirstr_desc* */
84#define RELEASE_DIRSTR(type, dir) \
85 ({ \
86 FILESTR_UNLOCK(type, &(dir)->stream); \
87 file_internal_unlock_##type(); \
88 })
89
90
91/* find a free dir stream descriptor */
92static struct dirstr_desc * alloc_dirstr(void)
93{
94 for (unsigned int dd = 0; dd < MAX_OPEN_DIRS; dd++)
95 {
96 struct dirstr_desc *dir = &open_streams[dd];
97 if (!dir->stream.flags)
98 return dir;
99 }
100
101 DEBUGF("Too many dirs open\n");
102 return NULL;
103}
104
105/** POSIX interface **/
106
107/* open a directory */
108DIR * opendir(const char *dirname)
109{
110 DEBUGF("opendir(dirname=\"%s\"\n", dirname);
111
112 DIR *dirp = NULL;
113
114 file_internal_lock_WRITER();
115
116 int rc;
117
118 struct dirstr_desc * const dir = alloc_dirstr();
119 if (!dir)
120 FILE_ERROR(EMFILE, RC);
121
122 rc = ns_open_stream(dirname, FF_DIR, &dir->stream, &dir->scan);
123 if (rc < 0)
124 {
125 DEBUGF("Open failed: %d\n", rc);
126 FILE_ERROR(ERRNO, RC);
127 }
128
129 dirp = (DIR *)dir;
130file_error:
131 file_internal_unlock_WRITER();
132 return dirp;
133}
134
135/* close a directory stream */
136int closedir(DIR *dirp)
137{
138 int rc;
139
140 file_internal_lock_WRITER();
141
142 /* needs to work even if marked "nonexistant" */
143 struct dirstr_desc * const dir = (struct dirstr_desc *)dirp;
144 if (!PTR_IN_ARRAY(open_streams, dir, MAX_OPEN_DIRS))
145 FILE_ERROR(EFAULT, -1);
146
147 if (!dir->stream.flags)
148 {
149 DEBUGF("dir #%d: dir not open\n", (int)(dir - open_streams));
150 FILE_ERROR(EBADF, -2);
151 }
152
153 rc = ns_close_stream(&dir->stream);
154 if (rc < 0)
155 FILE_ERROR(ERRNO, rc * 10 - 3);
156
157file_error:
158 file_internal_unlock_WRITER();
159 return rc;
160}
161
162/* read a directory */
163struct dirent * readdir(DIR *dirp)
164{
165 struct dirstr_desc * const dir = GET_DIRSTR(READER, dirp);
166 if (!dir)
167 FILE_ERROR_RETURN(ERRNO, NULL);
168
169 struct dirent *res = NULL;
170
171 int rc = ns_readdir_dirent(&dir->stream, &dir->scan, &dir->entry);
172 if (rc > 0)
173 res = &dir->entry;
174 else if (rc < 0)
175 FILE_ERROR(EIO, RC);
176
177file_error:
178 RELEASE_DIRSTR(READER, dir);
179
180 if (rc > 1)
181 iso_decode_d_name(res->d_name);
182
183 return res;
184}
185
186#if 0 /* not included now but probably should be */
187/* read a directory (reentrant) */
188int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
189{
190 if (!result)
191 FILE_ERROR_RETURN(EFAULT, -2);
192
193 *result = NULL;
194
195 if (!entry)
196 FILE_ERROR_RETURN(EFAULT, -3);
197
198 struct dirstr_desc * const dir = GET_DIRSTR(READER, dirp);
199 if (!dir)
200 FILE_ERROR_RETURN(ERRNO, -1);
201
202 int rc = ns_readdir_dirent(&dir->stream, &dir->scan, entry);
203 if (rc < 0)
204 FILE_ERROR(EIO, rc * 10 - 4);
205
206file_error:
207 RELEASE_DIRSTR(READER, dir);
208
209 if (rc > 0)
210 {
211 if (rc > 1)
212 iso_decode_d_name(entry->d_name);
213
214 *result = entry;
215 rc = 0;
216 }
217
218 return rc;
219}
220
221/* reset the position of a directory stream to the beginning of a directory */
222void rewinddir(DIR *dirp)
223{
224 struct dirstr_desc * const dir = GET_DIRSTR(READER, dirp);
225 if (!dir)
226 FILE_ERROR_RETURN(ERRNO);
227
228 ns_dirscan_rewind(&dir->scan);
229
230 RELEASE_DIRSTR(READER, dir);
231}
232
233#endif /* 0 */
234
235/* make a directory */
236int mkdir(const char *path)
237{
238 DEBUGF("mkdir(path=\"%s\")\n", path);
239
240 int rc;
241
242 file_internal_lock_WRITER();
243
244 struct filestr_base stream;
245 struct path_component_info compinfo;
246 rc = open_stream_internal(path, FF_DIR | FF_PARENTINFO, &stream,
247 &compinfo);
248 if (rc < 0)
249 {
250 DEBUGF("Can't open parent dir or path is not a directory\n");
251 FILE_ERROR(ERRNO, rc * 10 - 1);
252 }
253 else if (rc > 0)
254 {
255 DEBUGF("File exists\n");
256 FILE_ERROR(EEXIST, -2);
257 }
258
259 rc = create_stream_internal(&compinfo.parentinfo, compinfo.name,
260 compinfo.length, ATTR_NEW_DIRECTORY,
261 FO_DIRECTORY, &stream);
262 if (rc < 0)
263 FILE_ERROR(ERRNO, rc * 10 - 3);
264
265 rc = 0;
266file_error:
267 close_stream_internal(&stream);
268 file_internal_unlock_WRITER();
269 return rc;
270}
271
272/* remove a directory */
273int rmdir(const char *name)
274{
275 DEBUGF("rmdir(name=\"%s\")\n", name);
276
277 if (name)
278 {
279 /* path may not end with "." */
280 const char *basename;
281 size_t len = path_basename(name, &basename);
282 if (basename[0] == '.' && len == 1)
283 {
284 DEBUGF("Invalid path; last component is \".\"\n");
285 FILE_ERROR_RETURN(EINVAL, -9);
286 }
287 }
288
289 file_internal_lock_WRITER();
290 int rc = remove_stream_internal(name, NULL, FF_DIR);
291 file_internal_unlock_WRITER();
292 return rc;
293}
294
295
296/** Extended interface **/
297
298/* return if two directory streams refer to the same directory */
299int samedir(DIR *dirp1, DIR *dirp2)
300{
301 struct dirstr_desc * const dir1 = GET_DIRSTR(WRITER, dirp1);
302 if (!dir1)
303 FILE_ERROR_RETURN(ERRNO, -1);
304
305 int rc = -2;
306
307 struct dirstr_desc * const dir2 = get_dirstr(dirp2);
308 if (dir2)
309 rc = dir1->stream.bindp == dir2->stream.bindp ? 1 : 0;
310
311 RELEASE_DIRSTR(WRITER, dir1);
312 return rc;
313}
314
315/* test directory existence (returns 'false' if a file) */
316bool dir_exists(const char *dirname)
317{
318 file_internal_lock_WRITER();
319 bool rc = test_stream_exists_internal(dirname, FF_DIR) > 0;
320 file_internal_unlock_WRITER();
321 return rc;
322}
323
324/* get the portable info from the native entry */
325struct dirinfo dir_get_info(DIR *dirp, struct dirent *entry)
326{
327 int rc;
328 if (!dirp || !entry)
329 FILE_ERROR(EFAULT, RC);
330
331 if (entry->d_name[0] == '\0')
332 FILE_ERROR(ENOENT, RC);
333
334 if ((file_size_t)entry->info.size > FILE_SIZE_MAX)
335 FILE_ERROR(EOVERFLOW, RC);
336
337 return (struct dirinfo)
338 {
339 .attribute = entry->info.attr,
340 .size = entry->info.size,
341 .mtime = dostime_mktime(entry->info.wrtdate, entry->info.wrttime),
342 };
343
344file_error:
345 return (struct dirinfo){ .attribute = 0 };
346}
347
348const char* root_realpath(void)
349{
350 /* Native only, for APP and SIM see respective filesystem-.c files */
351 return root_get_realpath(); /* rb_namespace.c */
352}