A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 384 lines 12 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * $Id$ 9 * 10 * Copyright (C) 2017 by Michael Sevakis 11 * 12 * This program is free software; you can redistribute it and/or 13 * modify it under the terms of the GNU General Public License 14 * as published by the Free Software Foundation; either version 2 15 * of the License, or (at your option) any later version. 16 * 17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 18 * KIND, either express or implied. 19 * 20 ****************************************************************************/ 21#include "config.h" 22#include <errno.h> 23#include "fileobj_mgr.h" 24#include "rb_namespace.h" 25#include "file_internal.h" 26#include <stdio.h> /*snprintf*/ 27 28/* Define LOGF_ENABLE to enable logf output in this file */ 29//#define LOGF_ENABLE 30#include "logf.h" 31 32#if !defined(HAVE_MULTIVOLUME) && defined(LOGF_ENABLE) 33 int volume = 0; 34#endif 35 36 37#define ROOT_CONTENTS_INDEX (NUM_VOLUMES) 38#define NUM_ROOT_ITEMS (NUM_VOLUMES+1) 39 40static uint8_t root_entry_flags[NUM_VOLUMES+1]; 41static struct file_base_binding *root_bindp; 42 43static inline unsigned int get_root_item_state(int item) 44{ 45 return root_entry_flags[item]; 46} 47 48static inline void set_root_item_state(int item, unsigned int state) 49{ 50 root_entry_flags[item] = state; 51} 52 53static void get_mount_point_entry(IF_MV(int volume,) struct DIRENT *entry) 54{ 55#ifdef HAVE_MULTIVOLUME 56 get_volume_name(volume, entry->d_name); 57#else /* */ 58 strcpy(entry->d_name, PATH_ROOTSTR); 59#endif /* HAVE_MULTIVOLUME */ 60#if defined(_FILESYSTEM_NATIVE_H_) 61 entry->info.attr = ATTR_MOUNT_POINT; 62 entry->info.size = 0; 63 entry->info.wrtdate = 0; 64 entry->info.wrttime = 0; 65#endif /* is dirinfo_native */ 66 logf("%s: vol:%d, %s", __func__, volume, entry->d_name); 67} 68 69/* unmount the directory that enumerates into the root namespace */ 70static void unmount_item(int item) 71{ 72 unsigned int state = get_root_item_state(item); 73 logf("%s: state: %u", __func__, state); 74 if (!state) 75 return; 76 77 if (item == ROOT_CONTENTS_INDEX && state & NSITEM_CONTENTS) 78 { 79 fileobj_unmount(root_bindp); 80 root_bindp = NULL; 81 } 82 83 set_root_item_state(item, 0); 84} 85 86static char *root_realpath_internal(void) 87{ 88 static char root_realpath[ROOT_MAX_REALPATH]; 89 return root_realpath; 90} 91const char* root_get_realpath(void) 92{ 93 return root_realpath_internal(); 94} 95 96/* mount the directory that enumerates into the root namespace */ 97int root_mount_path(const char *path, unsigned int flags) 98{ 99 const char *folder = NULL; /* is a folder enumerated in the root? */ 100#ifdef HAVE_MULTIVOLUME 101 int volume = path_strip_volume(path, &folder, false); 102 if (volume == ROOT_VOLUME) 103 return -EINVAL; 104 if (!CHECK_VOL(volume)) 105 return -ENOENT; 106 char volname[VOL_MAX_LEN+2]; 107 make_volume_root(volume, volname); 108#else 109 const char *volname = PATH_ROOTSTR; 110 if (!path_is_absolute(path)) 111 { 112 logf("Path not absolute %s", path); 113 return -ENOENT; 114 } 115 path_dirname(path, &folder); 116#endif /* HAVE_MULTIVOLUME */ 117 bool contents = flags & NSITEM_CONTENTS; 118 int item = IF_MV_VOL(volume); 119 unsigned int state = get_root_item_state(item); 120 logf("%s: item:%d, st:%u, %s", __func__, item, state, path); 121 if (contents && state) /* volume must be mounted to enumerate into the root namespace */ 122 { 123 if (get_root_item_state(ROOT_CONTENTS_INDEX)) 124 return -EBUSY; /* error something is already enumerated */ 125 /* cache information about the target */ 126 struct filestr_base stream; 127 struct path_component_info compinfo; 128 int e = errno; 129 int rc = open_stream_internal(path, FF_DIR | FF_PROBE | FF_INFO | 130 FF_DEVPATH, &stream, &compinfo); 131 if (rc <= 0) 132 { 133 rc = rc ? -errno : -ENOENT; 134 errno = e; 135 return rc; 136 } 137 if (!fileobj_mount(&compinfo.info, FO_DIRECTORY, &root_bindp)) 138 return -EBUSY; 139 int root_state = NSITEM_MOUNTED | (flags & (NSITEM_HIDDEN|NSITEM_CONTENTS)); 140 set_root_item_state(ROOT_CONTENTS_INDEX, root_state); 141 flags |= state; /* preserve the state of the mounted volume */ 142 143 if (folder) 144 { 145 while (*folder == PATH_SEPCH) 146 folder++; 147 /*if a folder has been enumerated don't mark the whole volume */ 148 if (folder[0] != '\0') 149 flags &= ~NSITEM_CONTENTS; 150 else 151 folder = NULL; /*Ensure separator is added by path_append */ 152 } 153 154 path_append(root_realpath_internal(), volname, folder, ROOT_MAX_REALPATH); 155 } 156 else if (state) /* error volume already mounted */ 157 return -EBUSY; 158 state = NSITEM_MOUNTED | (flags & (NSITEM_HIDDEN|NSITEM_CONTENTS)); 159 set_root_item_state(item, state); 160 return 0; 161} 162 163/* check if volume in path is mounted in the root namespace */ 164bool ns_volume_is_visible(IF_MV_NONVOID(int volume)) 165{ 166 int item = IF_MV_VOL(volume); 167 if ((item == ROOT_VOLUME) || !CHECK_VOL(item)) 168 return false; 169 unsigned int state = get_root_item_state(item); 170 return state && (((state & NSITEM_HIDDEN) == 0) || (state & NSITEM_CONTENTS)); 171} 172 173/* inform root that an entire volume is being unmounted */ 174void root_unmount_volume(IF_MV_NONVOID(int volume)) 175{ 176 logf("%s: vol: %d", __func__, volume); 177 FOR_EACH_VOLUME(volume, item) 178 { 179 #ifdef HAVE_MULTIVOLUME 180 uint32_t state = get_root_item_state(item); 181 if (state && (volume < 0 || item == volume)) 182 #endif /* HAVE_MULTIVOLUME */ 183 unmount_item(item); 184 } 185 186 /* if the volume unmounted contains the root directory contents then 187 the contents must also be unmounted */ 188#ifdef HAVE_MULTIVOLUME 189 uint32_t state = get_root_item_state(ROOT_CONTENTS_INDEX); 190 if (state && (volume < 0 || BASEINFO_VOL(&root_bindp->info) == volume)) 191#endif 192 { 193 unmount_item(ROOT_CONTENTS_INDEX); 194 root_realpath_internal()[0] = '\0'; 195 } 196} 197 198/* parse the root part of a path */ 199int ns_parse_root(const char *path, const char **pathp, uint16_t *lenp) 200{ 201 logf("%s: path: %s", __func__, path); 202 int volume = ROOT_VOLUME; 203 204#ifdef HAVE_MULTIVOLUME 205 /* this seamlessly integrates secondary filesystems into the 206 root namespace (e.g. "/<0>/../../<1>/../foo/." :<=> "/foo") */ 207 const char *p; 208 volume = path_strip_volume(path, &p, false); 209 if (volume != ROOT_VOLUME && !CHECK_VOL(volume)) 210 { 211 logf("vol: %d is not root", volume); 212 return -ENOENT; 213 } 214#endif /* HAVE_MULTIVOLUME */ 215 216 /* set name to start at last leading separator; name of root will 217 * be returned as "/", volume specifiers as "/<fooN>" */ 218 *pathp = GOBBLE_PATH_SEPCH(path) - 1; 219 *lenp = IF_MV( volume < NUM_VOLUMES ? p - *pathp : ) 1; 220#ifdef LOGF_ENABLE 221 if (volume == INT_MAX) 222 logf("vol: ROOT(%d) %s", volume, *pathp); 223 else 224 logf("vol: %d %s", volume, *pathp); 225#endif 226#ifdef HAVE_MULTIVOLUME 227 if (*lenp > MAX_COMPNAME+1) 228 { 229 logf("%s: path too long %s", __func__, path); 230 return -ENAMETOOLONG; 231 } 232#endif 233#ifdef LOGF_ENABLE 234 if (volume == INT_MAX) 235 logf("%s: vol: ROOT(%d) path: %s", __func__, volume, path); 236 else 237 logf("%s: vol: %d path: %s", __func__, volume, path); 238#endif 239 return volume; 240} 241 242/* open one of the items in the root */ 243int ns_open_root(IF_MV(int volume,) unsigned int *callflagsp, 244 struct file_base_info *infop, uint16_t *attrp) 245{ 246 unsigned int callflags = *callflagsp; 247 bool devpath = !!(callflags & FF_DEVPATH); 248#ifdef HAVE_MULTIVOLUME 249 bool sysroot = volume == ROOT_VOLUME; 250 if (devpath && sysroot) 251 return -ENOENT; /* devpath needs volume spec */ 252#else 253 bool sysroot = !devpath; /* always sysroot unless devpath */ 254#endif 255 256 int item = sysroot ? ROOT_CONTENTS_INDEX : IF_MV_VOL(volume); 257 unsigned int state = get_root_item_state(item); 258 logf("%s: Vol:%d St:%d", __func__, item, state); 259 if (sysroot) 260 { 261 *attrp = ATTR_SYSTEM_ROOT; 262 263 if (state) 264 *infop = root_bindp->info; 265 else 266 { 267 logf("%s: SysRoot Vol:%d St:%d NOT mounted", __func__, item, state); 268 *callflagsp = callflags | FF_NOFS; /* contents not mounted */ 269 } 270 } 271 else 272 { 273 *attrp = ATTR_MOUNT_POINT; 274 275 if (!devpath && !state) 276 return -ENOENT; /* regular open requires having been mounted */ 277#if CONFIG_PLATFORM & PLATFORM_NATIVE 278 if (fat_open_rootdir(IF_MV(volume,) &infop->fatfile) < 0) 279 { 280 logf("%s: DevPath Vol:%d St:%d NOT mounted", __func__, item, state); 281 return -ENOENT; /* not mounted */ 282 } 283#endif 284 get_rootinfo_internal(infop); 285 } 286 287 return 0; 288} 289 290/* read root directory entries */ 291int root_readdir_dirent(struct filestr_base *stream, 292 struct ns_scan_info *scanp, struct DIRENT *entry) 293{ 294 int rc = 0; 295 296 int item = scanp->item; 297 logf("%s: item: %d", __func__, item); 298 299 /* skip any not-mounted or hidden items */ 300 unsigned int state; 301 while (1) 302 { 303 if (item >= NUM_ROOT_ITEMS) 304 goto file_eod; 305 306 state = get_root_item_state(item); 307 if ((state & (NSITEM_MOUNTED|NSITEM_HIDDEN)) == NSITEM_MOUNTED) 308 { 309#if 1 /* hide the volume enumerated into the root namespace */ 310 if (item == ROOT_CONTENTS_INDEX || (state & NSITEM_CONTENTS) == 0) 311 { 312 logf("Found mounted item: %d %s", item, entry->d_name); 313 break; 314 } 315#endif 316 } 317 318 item++; 319 } 320 321 if (item == ROOT_CONTENTS_INDEX) 322 { 323 rc = readdir_dirent(stream, &scanp->scan, entry); 324 if (rc < 0) 325 FILE_ERROR(ERRNO, rc * 10 - 1); 326 327 if (rc == 0) 328 { 329 logf("Found root item: %d %s", item, entry->d_name); 330 item++; 331 } 332 } 333 else 334 { 335 get_mount_point_entry(IF_MV(item,) entry); 336 item++; 337 rc = 1; 338 logf("Found mp item:%d %s", item, entry->d_name); 339 } 340 341 scanp->item = item; 342 343file_eod: 344#ifdef HAVE_DIRCACHE 345 if (rc == 0) 346 empty_dirent(entry); 347#endif 348file_error: 349 logf("%s: status: %d", __func__, rc); 350 return rc; 351} 352 353/* opens a stream to enumerate items in a namespace container */ 354int ns_open_stream(const char *path, unsigned int callflags, 355 struct filestr_base *stream, struct ns_scan_info *scanp) 356{ 357 logf("%s: path: %s", __func__, path); 358 /* stream still needs synchronization even if we don't have a stream */ 359 static struct mutex no_contents_mtx SHAREDBSS_ATTR; 360 361 int rc = open_stream_internal(path, callflags, stream, NULL); 362 if (rc < 0) 363 FILE_ERROR(ERRNO, rc * 10 - 1); 364 365 scanp->item = rc > 1 ? 0 : -1; 366 367 if (stream->flags & FDO_BUSY) 368 { 369 /* root contents are mounted */ 370 fat_rewind(&stream->fatstr); 371 } 372 else 373 { 374 /* root contents not mounted */ 375 mutex_init(&no_contents_mtx); 376 stream->mtx = &no_contents_mtx; 377 } 378 379 ns_dirscan_rewind(scanp); 380 381 rc = 0; 382file_error: 383 return rc; 384}