freemyipod r60 - Code Review

Jump to: navigation, search
Repository:freemyipod
Revision:r59‎ | r60 | r61 >
Date:00:29, 9 August 2010
Author:theseven
Status:new
Tags:
Comment:
Fix some more includes
Modified paths:
  • /embios/trunk/dir.c (modified) (history)
  • /embios/trunk/fat.c (modified) (history)
  • /embios/trunk/global.h (modified) (history)

Diff [purge]

Index: embios/trunk/global.h
@@ -28,7 +28,9 @@
2929 #include <stdint.h>
3030 #include <stddef.h>
3131 #include <stdbool.h>
32 -#include <stdio.h>
 32+typedef int32_t ssize_t;
 33+typedef int32_t mode_t;
 34+typedef int32_t off_t;
3335 #endif
3436
3537 #include "build/version.h"
Index: embios/trunk/fat.c
@@ -20,13 +20,12 @@
2121 ****************************************************************************/
2222 #include "global.h"
2323 #include "thread.h"
24 -#include <stdio.h>
2524 #include <string.h>
26 -#include <ctype.h>
2725 #include "fat.h"
2826 #include "storage.h"
2927 #include "debug.h"
3028 #include "panic.h"
 29+#include "snprintf.h"
3130
3231 #define BYTES2INT16(array,pos) \
3332 (array[pos] | (array[pos+1] << 8 ))
Index: embios/trunk/dir.c
@@ -1,337 +1,336 @@
2 -/***************************************************************************
3 - * __________ __ ___.
4 - * Open \______ \ ____ ____ | | _\_ |__ _______ ___
5 - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
6 - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
7 - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
8 - * \/ \/ \/ \/ \/
9 - * $Id: dir.c 13741 2007-06-30 02:08:27Z jethead71 $
10 - *
11 - * Copyright (C) 2002 by Björn Stenberg
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 -#include "global.h"
23 -#include <stdio.h>
24 -#include <errno.h>
25 -#include <string.h>
26 -#include "fat.h"
27 -#include "dir.h"
28 -#include "debug.h"
29 -
30 -#ifndef MAX_OPEN_DIRS
31 -#define MAX_OPEN_DIRS 16
32 -#endif
33 -
 2+/***************************************************************************
 3+ * __________ __ ___.
 4+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
 5+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
 6+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
 7+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
 8+ * \/ \/ \/ \/ \/
 9+ * $Id: dir.c 13741 2007-06-30 02:08:27Z jethead71 $
 10+ *
 11+ * Copyright (C) 2002 by Björn Stenberg
 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+#include "global.h"
 23+#include <errno.h>
 24+#include <string.h>
 25+#include "fat.h"
 26+#include "dir.h"
 27+#include "debug.h"
 28+
 29+#ifndef MAX_OPEN_DIRS
 30+#define MAX_OPEN_DIRS 16
 31+#endif
 32+
3433 extern struct scheduler_thread* current_thread;
35 -static DIR opendirs[MAX_OPEN_DIRS];
36 -
37 -#ifdef HAVE_HOTSWAP
38 -// release all dir handles on a given volume "by force", to avoid leaks
39 -int release_dirs(int volume)
40 -{
41 - DIR* pdir = opendirs;
42 - int dd;
43 - int closed = 0;
44 - for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++)
45 - {
46 -#ifdef HAVE_MULTIVOLUME
47 - if (pdir->fatdir.file.volume == volume)
48 -#else
49 - (void)volume;
50 -#endif
51 - {
52 - pdir->busy = false; /* mark as available, no further action */
53 - closed++;
54 - }
55 - }
56 - return closed; /* return how many we did */
57 -}
58 -#endif /* #ifdef HAVE_HOTSWAP */
59 -
60 -DIR* opendir(const char* name)
61 -{
62 - char namecopy[MAX_PATH];
63 - char* part;
64 - char* end;
65 - struct fat_direntry entry;
66 - int dd;
67 - DIR* pdir = opendirs;
68 -#ifdef HAVE_MULTIVOLUME
69 - int volume;
70 -#endif
71 -
72 - if ( name[0] != '/' ) {
73 - DEBUGF("Only absolute paths supported right now");
74 - return NULL;
75 - }
76 -
77 - /* find a free dir descriptor */
78 - for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++)
79 - if ( !pdir->busy )
80 - break;
81 -
82 - if ( dd == MAX_OPEN_DIRS ) {
83 - DEBUGF("Too many dirs open");
84 - errno = EMFILE;
85 - return NULL;
86 - }
87 -
88 - pdir->busy = true;
89 - pdir->process = current_thread;
90 -
91 -#ifdef HAVE_MULTIVOLUME
92 - /* try to extract a heading volume name, if present */
93 - volume = strip_volume(name, namecopy);
94 - pdir->volumecounter = 0;
95 -#else
96 - strlcpy(namecopy, name, sizeof(namecopy)); /* just copy */
97 -#endif
98 -
99 - if ( fat_opendir(IF_MV2(volume,) &pdir->fatdir, 0, NULL) < 0 ) {
100 - DEBUGF("Failed opening root dir");
101 - pdir->busy = false;
102 - return NULL;
103 - }
104 -
105 - for ( part = strtok_r(namecopy, "/", &end); part;
106 - part = strtok_r(NULL, "/", &end)) {
107 - /* scan dir for name */
108 - while (1) {
109 - if ((fat_getnext(&pdir->fatdir,&entry) < 0) ||
110 - (!entry.name[0])) {
111 - pdir->busy = false;
112 - return NULL;
113 - }
114 - if ( (entry.attr & FAT_ATTR_DIRECTORY) &&
115 - (!strcasecmp(part, entry.name)) ) {
116 - /* In reality, the parent_dir parameter of fat_opendir seems
117 - * useless because it's sole purpose it to have a way to
118 - * update the file metadata, but here we are only reading
119 - * a directory so there's no need for that kind of stuff.
120 - * However, the rmdir function uses a ugly hack to
121 - * avoid opening a directory twice when deleting it and thus
122 - * needs those information. That's why we pass pdir->fatdir both
123 - * as the parent directory and the resulting one (this is safe,
124 - * in doubt, check fat_open(dir) code) which will allow this kind of
125 - * (ugly) things */
126 - if ( fat_opendir(IF_MV2(volume,)
127 - &pdir->fatdir,
128 - entry.firstcluster,
129 - &pdir->fatdir) < 0 ) {
130 - DEBUGF("Failed opening dir '%s' (%ld)",
131 - part, entry.firstcluster);
132 - pdir->busy = false;
133 - return NULL;
134 - }
135 -#ifdef HAVE_MULTIVOLUME
136 - pdir->volumecounter = -1; /* n.a. to subdirs */
137 -#endif
138 - break;
139 - }
140 - }
141 - }
142 -
143 - return pdir;
144 -}
145 -
146 -int closedir(DIR* dir)
147 -{
148 - dir->busy=false;
149 - return 0;
150 -}
151 -
152 -int closedir_all_of_process(struct scheduler_thread* process)
153 -{
154 - DIR* pdir = opendirs;
155 - int dd;
156 - int closed = 0;
157 - for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++)
158 - {
159 - if (pdir->process == process)
160 - {
161 - pdir->busy = false; /* mark as available, no further action */
162 - closed++;
163 - }
164 - }
165 - return closed; /* return how many we did */
166 -}
167 -
168 -struct dirent* readdir(DIR* dir)
169 -{
170 - struct fat_direntry entry;
171 - struct dirent* theent = &(dir->theent);
172 -
173 - if (!dir->busy)
174 - return NULL;
175 -
176 -#ifdef HAVE_MULTIVOLUME
177 - /* Volumes (secondary file systems) get inserted into the root directory
178 - of the first volume, since we have no separate top level. */
179 - if (dir->volumecounter >= 0 /* on a root dir */
180 - && dir->volumecounter < NUM_VOLUMES /* in range */
181 - && dir->fatdir.file.volume == 0) /* at volume 0 */
182 - { /* fake special directories, which don't really exist, but
183 - will get redirected upon opendir() */
184 - while (++dir->volumecounter < NUM_VOLUMES)
185 - {
186 - if (fat_ismounted(dir->volumecounter))
187 - {
188 - memset(theent, 0, sizeof(*theent));
189 - theent->attribute = FAT_ATTR_DIRECTORY | FAT_ATTR_VOLUME;
190 - snprintf(theent->d_name, sizeof(theent->d_name),
191 - VOL_NAMES, dir->volumecounter);
192 - return theent;
193 - }
194 - }
195 - }
196 -#endif
197 - /* normal directory entry fetching follows here */
198 - if (fat_getnext(&(dir->fatdir),&entry) < 0)
199 - return NULL;
200 -
201 - if ( !entry.name[0] )
202 - return NULL;
203 -
204 - strlcpy(theent->d_name, entry.name, sizeof(theent->d_name));
205 - theent->attribute = entry.attr;
206 - theent->size = entry.filesize;
207 - theent->startcluster = entry.firstcluster;
208 - theent->wrtdate = entry.wrtdate;
209 - theent->wrttime = entry.wrttime;
210 -
211 - return theent;
212 -}
213 -
214 -int mkdir(const char *name)
215 -{
216 - DIR *dir;
217 - char namecopy[MAX_PATH];
218 - char* end;
219 - char *basename;
220 - char *parent;
221 - struct dirent *entry;
222 - struct fat_dir newdir;
223 - int rc;
224 -
225 - if ( name[0] != '/' ) {
226 - DEBUGF("mkdir: Only absolute paths supported right now");
227 - return -1;
228 - }
229 -
230 - strlcpy(namecopy, name, sizeof(namecopy));
231 -
232 - /* Split the base name and the path */
233 - end = strrchr(namecopy, '/');
234 - *end = 0;
235 - basename = end+1;
236 -
237 - if(namecopy == end) /* Root dir? */
238 - parent = "/";
239 - else
240 - parent = namecopy;
241 -
242 - DEBUGF("mkdir: parent: %s, name: %s", parent, basename);
243 -
244 - dir = opendir(parent);
245 -
246 - if(!dir) {
247 - DEBUGF("mkdir: can't open parent dir");
248 - return -2;
249 - }
250 -
251 - if(basename[0] == 0) {
252 - DEBUGF("mkdir: Empty dir name");
253 - errno = EINVAL;
254 - return -3;
255 - }
256 -
257 - /* Now check if the name already exists */
258 - while ((entry = readdir(dir))) {
259 - if ( !strcasecmp(basename, entry->d_name) ) {
260 - DEBUGF("mkdir error: file exists");
261 - errno = EEXIST;
262 - closedir(dir);
263 - return - 4;
264 - }
265 - }
266 -
267 - memset(&newdir, 0, sizeof(struct fat_dir));
268 -
269 - rc = fat_create_dir(basename, &newdir, &(dir->fatdir));
270 - closedir(dir);
271 -
272 - return rc;
273 -}
274 -
275 -int rmdir(const char* name)
276 -{
277 - int rc;
278 - DIR* dir;
279 - struct dirent* entry;
280 -
281 - dir = opendir(name);
282 - if (!dir)
283 - {
284 - errno = ENOENT; /* open error */
285 - return -1;
286 - }
287 -
288 - /* check if the directory is empty */
289 - while ((entry = readdir(dir)))
290 - {
291 - if (strcmp(entry->d_name, ".") &&
292 - strcmp(entry->d_name, ".."))
293 - {
294 - DEBUGF("rmdir error: not empty");
295 - errno = ENOTEMPTY;
296 - closedir(dir);
297 - return -2;
298 - }
299 - }
300 -
301 - rc = fat_remove(&(dir->fatdir.file));
302 - if ( rc < 0 ) {
303 - DEBUGF("Failed removing dir: %d", rc);
304 - errno = EIO;
305 - rc = rc * 10 - 3;
306 - }
307 -
308 - closedir(dir);
309 - return rc;
310 -}
311 -
312 -#ifdef HAVE_MULTIVOLUME
313 -/* returns on which volume this is, and copies the reduced name
314 - (sortof a preprocessor for volume-decorated pathnames) */
315 -int strip_volume(const char* name, char* namecopy)
316 -{
317 - int volume = 0;
318 - const char *temp = name;
319 -
320 - while (*temp == '/') /* skip all leading slashes */
321 - ++temp;
322 -
323 - if (*temp && !strncmp(temp, VOL_NAMES, VOL_ENUM_POS))
324 - {
325 - temp += VOL_ENUM_POS; /* behind special name */
326 - volume = atoi(temp); /* number is following */
327 - temp = strchr(temp, '/'); /* search for slash behind */
328 - if (temp != NULL)
329 - name = temp; /* use the part behind the volume */
330 - else
331 - name = "/"; /* else this must be the root dir */
332 - }
333 -
334 - strlcpy(namecopy, name, MAX_PATH);
335 -
336 - return volume;
337 -}
338 -#endif /* #ifdef HAVE_MULTIVOLUME */
 34+static DIR opendirs[MAX_OPEN_DIRS];
 35+
 36+#ifdef HAVE_HOTSWAP
 37+// release all dir handles on a given volume "by force", to avoid leaks
 38+int release_dirs(int volume)
 39+{
 40+ DIR* pdir = opendirs;
 41+ int dd;
 42+ int closed = 0;
 43+ for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++)
 44+ {
 45+#ifdef HAVE_MULTIVOLUME
 46+ if (pdir->fatdir.file.volume == volume)
 47+#else
 48+ (void)volume;
 49+#endif
 50+ {
 51+ pdir->busy = false; /* mark as available, no further action */
 52+ closed++;
 53+ }
 54+ }
 55+ return closed; /* return how many we did */
 56+}
 57+#endif /* #ifdef HAVE_HOTSWAP */
 58+
 59+DIR* opendir(const char* name)
 60+{
 61+ char namecopy[MAX_PATH];
 62+ char* part;
 63+ char* end;
 64+ struct fat_direntry entry;
 65+ int dd;
 66+ DIR* pdir = opendirs;
 67+#ifdef HAVE_MULTIVOLUME
 68+ int volume;
 69+#endif
 70+
 71+ if ( name[0] != '/' ) {
 72+ DEBUGF("Only absolute paths supported right now");
 73+ return NULL;
 74+ }
 75+
 76+ /* find a free dir descriptor */
 77+ for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++)
 78+ if ( !pdir->busy )
 79+ break;
 80+
 81+ if ( dd == MAX_OPEN_DIRS ) {
 82+ DEBUGF("Too many dirs open");
 83+ errno = EMFILE;
 84+ return NULL;
 85+ }
 86+
 87+ pdir->busy = true;
 88+ pdir->process = current_thread;
 89+
 90+#ifdef HAVE_MULTIVOLUME
 91+ /* try to extract a heading volume name, if present */
 92+ volume = strip_volume(name, namecopy);
 93+ pdir->volumecounter = 0;
 94+#else
 95+ strlcpy(namecopy, name, sizeof(namecopy)); /* just copy */
 96+#endif
 97+
 98+ if ( fat_opendir(IF_MV2(volume,) &pdir->fatdir, 0, NULL) < 0 ) {
 99+ DEBUGF("Failed opening root dir");
 100+ pdir->busy = false;
 101+ return NULL;
 102+ }
 103+
 104+ for ( part = strtok_r(namecopy, "/", &end); part;
 105+ part = strtok_r(NULL, "/", &end)) {
 106+ /* scan dir for name */
 107+ while (1) {
 108+ if ((fat_getnext(&pdir->fatdir,&entry) < 0) ||
 109+ (!entry.name[0])) {
 110+ pdir->busy = false;
 111+ return NULL;
 112+ }
 113+ if ( (entry.attr & FAT_ATTR_DIRECTORY) &&
 114+ (!strcasecmp(part, entry.name)) ) {
 115+ /* In reality, the parent_dir parameter of fat_opendir seems
 116+ * useless because it's sole purpose it to have a way to
 117+ * update the file metadata, but here we are only reading
 118+ * a directory so there's no need for that kind of stuff.
 119+ * However, the rmdir function uses a ugly hack to
 120+ * avoid opening a directory twice when deleting it and thus
 121+ * needs those information. That's why we pass pdir->fatdir both
 122+ * as the parent directory and the resulting one (this is safe,
 123+ * in doubt, check fat_open(dir) code) which will allow this kind of
 124+ * (ugly) things */
 125+ if ( fat_opendir(IF_MV2(volume,)
 126+ &pdir->fatdir,
 127+ entry.firstcluster,
 128+ &pdir->fatdir) < 0 ) {
 129+ DEBUGF("Failed opening dir '%s' (%ld)",
 130+ part, entry.firstcluster);
 131+ pdir->busy = false;
 132+ return NULL;
 133+ }
 134+#ifdef HAVE_MULTIVOLUME
 135+ pdir->volumecounter = -1; /* n.a. to subdirs */
 136+#endif
 137+ break;
 138+ }
 139+ }
 140+ }
 141+
 142+ return pdir;
 143+}
 144+
 145+int closedir(DIR* dir)
 146+{
 147+ dir->busy=false;
 148+ return 0;
 149+}
 150+
 151+int closedir_all_of_process(struct scheduler_thread* process)
 152+{
 153+ DIR* pdir = opendirs;
 154+ int dd;
 155+ int closed = 0;
 156+ for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++)
 157+ {
 158+ if (pdir->process == process)
 159+ {
 160+ pdir->busy = false; /* mark as available, no further action */
 161+ closed++;
 162+ }
 163+ }
 164+ return closed; /* return how many we did */
 165+}
 166+
 167+struct dirent* readdir(DIR* dir)
 168+{
 169+ struct fat_direntry entry;
 170+ struct dirent* theent = &(dir->theent);
 171+
 172+ if (!dir->busy)
 173+ return NULL;
 174+
 175+#ifdef HAVE_MULTIVOLUME
 176+ /* Volumes (secondary file systems) get inserted into the root directory
 177+ of the first volume, since we have no separate top level. */
 178+ if (dir->volumecounter >= 0 /* on a root dir */
 179+ && dir->volumecounter < NUM_VOLUMES /* in range */
 180+ && dir->fatdir.file.volume == 0) /* at volume 0 */
 181+ { /* fake special directories, which don't really exist, but
 182+ will get redirected upon opendir() */
 183+ while (++dir->volumecounter < NUM_VOLUMES)
 184+ {
 185+ if (fat_ismounted(dir->volumecounter))
 186+ {
 187+ memset(theent, 0, sizeof(*theent));
 188+ theent->attribute = FAT_ATTR_DIRECTORY | FAT_ATTR_VOLUME;
 189+ snprintf(theent->d_name, sizeof(theent->d_name),
 190+ VOL_NAMES, dir->volumecounter);
 191+ return theent;
 192+ }
 193+ }
 194+ }
 195+#endif
 196+ /* normal directory entry fetching follows here */
 197+ if (fat_getnext(&(dir->fatdir),&entry) < 0)
 198+ return NULL;
 199+
 200+ if ( !entry.name[0] )
 201+ return NULL;
 202+
 203+ strlcpy(theent->d_name, entry.name, sizeof(theent->d_name));
 204+ theent->attribute = entry.attr;
 205+ theent->size = entry.filesize;
 206+ theent->startcluster = entry.firstcluster;
 207+ theent->wrtdate = entry.wrtdate;
 208+ theent->wrttime = entry.wrttime;
 209+
 210+ return theent;
 211+}
 212+
 213+int mkdir(const char *name)
 214+{
 215+ DIR *dir;
 216+ char namecopy[MAX_PATH];
 217+ char* end;
 218+ char *basename;
 219+ char *parent;
 220+ struct dirent *entry;
 221+ struct fat_dir newdir;
 222+ int rc;
 223+
 224+ if ( name[0] != '/' ) {
 225+ DEBUGF("mkdir: Only absolute paths supported right now");
 226+ return -1;
 227+ }
 228+
 229+ strlcpy(namecopy, name, sizeof(namecopy));
 230+
 231+ /* Split the base name and the path */
 232+ end = strrchr(namecopy, '/');
 233+ *end = 0;
 234+ basename = end+1;
 235+
 236+ if(namecopy == end) /* Root dir? */
 237+ parent = "/";
 238+ else
 239+ parent = namecopy;
 240+
 241+ DEBUGF("mkdir: parent: %s, name: %s", parent, basename);
 242+
 243+ dir = opendir(parent);
 244+
 245+ if(!dir) {
 246+ DEBUGF("mkdir: can't open parent dir");
 247+ return -2;
 248+ }
 249+
 250+ if(basename[0] == 0) {
 251+ DEBUGF("mkdir: Empty dir name");
 252+ errno = EINVAL;
 253+ return -3;
 254+ }
 255+
 256+ /* Now check if the name already exists */
 257+ while ((entry = readdir(dir))) {
 258+ if ( !strcasecmp(basename, entry->d_name) ) {
 259+ DEBUGF("mkdir error: file exists");
 260+ errno = EEXIST;
 261+ closedir(dir);
 262+ return - 4;
 263+ }
 264+ }
 265+
 266+ memset(&newdir, 0, sizeof(struct fat_dir));
 267+
 268+ rc = fat_create_dir(basename, &newdir, &(dir->fatdir));
 269+ closedir(dir);
 270+
 271+ return rc;
 272+}
 273+
 274+int rmdir(const char* name)
 275+{
 276+ int rc;
 277+ DIR* dir;
 278+ struct dirent* entry;
 279+
 280+ dir = opendir(name);
 281+ if (!dir)
 282+ {
 283+ errno = ENOENT; /* open error */
 284+ return -1;
 285+ }
 286+
 287+ /* check if the directory is empty */
 288+ while ((entry = readdir(dir)))
 289+ {
 290+ if (strcmp(entry->d_name, ".") &&
 291+ strcmp(entry->d_name, ".."))
 292+ {
 293+ DEBUGF("rmdir error: not empty");
 294+ errno = ENOTEMPTY;
 295+ closedir(dir);
 296+ return -2;
 297+ }
 298+ }
 299+
 300+ rc = fat_remove(&(dir->fatdir.file));
 301+ if ( rc < 0 ) {
 302+ DEBUGF("Failed removing dir: %d", rc);
 303+ errno = EIO;
 304+ rc = rc * 10 - 3;
 305+ }
 306+
 307+ closedir(dir);
 308+ return rc;
 309+}
 310+
 311+#ifdef HAVE_MULTIVOLUME
 312+/* returns on which volume this is, and copies the reduced name
 313+ (sortof a preprocessor for volume-decorated pathnames) */
 314+int strip_volume(const char* name, char* namecopy)
 315+{
 316+ int volume = 0;
 317+ const char *temp = name;
 318+
 319+ while (*temp == '/') /* skip all leading slashes */
 320+ ++temp;
 321+
 322+ if (*temp && !strncmp(temp, VOL_NAMES, VOL_ENUM_POS))
 323+ {
 324+ temp += VOL_ENUM_POS; /* behind special name */
 325+ volume = atoi(temp); /* number is following */
 326+ temp = strchr(temp, '/'); /* search for slash behind */
 327+ if (temp != NULL)
 328+ name = temp; /* use the part behind the volume */
 329+ else
 330+ name = "/"; /* else this must be the root dir */
 331+ }
 332+
 333+ strlcpy(namecopy, name, MAX_PATH);
 334+
 335+ return volume;
 336+}
 337+#endif /* #ifdef HAVE_MULTIVOLUME */