A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 352 lines 8.9 kB view raw
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}