1 /*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2005 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU GPL.
6 See the file COPYING.
7 */
8
9 #include <mtpfs.h>
10 #include <glib/gprintf.h>
11 #include <stdlib.h> /* strtoul() */
12
13 #if DEBUG
14 #define STRINGIFY(x) #x
15 #define TOSTRING(x) STRINGIFY(x)
16 #define DBG(a...) {g_printf( "[" __FILE__ ":" TOSTRING(__LINE__) "] " a );g_printf("\n");}
17 #else
18 #define DBG(a...)
19 #endif
20
21 #if DEBUG
22 static void
dump_mtp_error()23 dump_mtp_error ()
24 {
25 LIBMTP_Dump_Errorstack (device);
26 LIBMTP_Clear_Errorstack (device);
27 }
28 #else
29 #define dump_mtp_error()
30 #endif
31
32 #define enter_lock(a...) do { DBG("lock"); DBG(a); g_mutex_lock(&device_lock); } while(0)
33 #define return_unlock(a) do { DBG("return unlock"); g_mutex_unlock(&device_lock); return a; } while(0)
34
35 void
free_files(LIBMTP_file_t * filelist)36 free_files (LIBMTP_file_t * filelist)
37 {
38 LIBMTP_file_t *file = filelist, *tmp;
39 while (file)
40 {
41 tmp = file;
42 file = file->next;
43 LIBMTP_destroy_file_t (tmp);
44 }
45 }
46
47 void
free_playlists(LIBMTP_playlist_t * pl)48 free_playlists (LIBMTP_playlist_t * pl)
49 {
50 LIBMTP_playlist_t *playlist = pl, *tmp;
51 while (playlist)
52 {
53 tmp = playlist;
54 playlist = playlist->next;
55 LIBMTP_destroy_playlist_t (tmp);
56 }
57 }
58
59 void
check_files()60 check_files ()
61 {
62 if (files_changed)
63 {
64 DBG ("Refreshing Filelist");
65 LIBMTP_file_t *newfiles = NULL;
66 if (files)
67 free_files (files);
68 newfiles = LIBMTP_Get_Filelisting_With_Callback (device, NULL, NULL);
69 files = newfiles;
70 newfiles = NULL;
71 files_changed = FALSE;
72 //check_lost_files ();
73 DBG ("Refreshing Filelist exiting");
74 }
75 }
76
77 static void
check_lost_files()78 check_lost_files ()
79 {
80 int last_parent_id = -1;
81 gboolean last_parent_found = FALSE;
82 LIBMTP_file_t *item;
83
84 if (lostfiles != NULL)
85 g_slist_free (lostfiles);
86
87 lostfiles = NULL;
88 for (item = files; item != NULL; item = item->next)
89 {
90 gboolean parent_found;
91
92 if (last_parent_id == -1 || last_parent_id != item->parent_id)
93 {
94 if (item->parent_id == 0)
95 {
96 parent_found = TRUE;
97 }
98 else
99 {
100 int i;
101 for (i = 0; i < 4; i++)
102 {
103 if (storageArea[i].folders != NULL)
104 {
105 if (LIBMTP_Find_Folder
106 (storageArea[i].folders, item->parent_id) != NULL)
107 {
108 parent_found = FALSE;
109 }
110 }
111 }
112 }
113 last_parent_id = item->parent_id;
114 last_parent_found = parent_found;
115 }
116 else
117 {
118 parent_found = last_parent_found;
119 }
120 DBG ("MTPFS checking for lost files %s, parent %d - %s",
121 item->filename, last_parent_id, (parent_found ? "FALSE" : "TRUE"));
122 if (parent_found == FALSE)
123 {
124 lostfiles = g_slist_append (lostfiles, item);
125 }
126 }
127 DBG ("MTPFS checking for lost files exit found %d lost tracks",
128 g_slist_length (lostfiles));
129 }
130
131 void
check_folders()132 check_folders ()
133 {
134 int i;
135 for (i = 0; i < 4; i++)
136 {
137 if (storageArea[i].folders_changed)
138 {
139 DBG ("Refreshing Folderlist %d-%s", i,
140 storageArea[i].storage->StorageDescription);
141 LIBMTP_folder_t *newfolders = NULL;
142 if (storageArea[i].folders)
143 {
144 LIBMTP_destroy_folder_t (storageArea[i].folders);
145 }
146 newfolders =
147 LIBMTP_Get_Folder_List_For_Storage (device,
148 storageArea[i].storage->id);
149 storageArea[i].folders = newfolders;
150 newfolders = NULL;
151 storageArea[i].folders_changed = FALSE;
152 }
153 }
154 }
155
156 void
check_playlists()157 check_playlists ()
158 {
159 if (playlists_changed)
160 {
161 DBG ("Refreshing Playlists");
162 LIBMTP_playlist_t *newplaylists;
163 if (playlists)
164 free_playlists (playlists);
165 newplaylists = LIBMTP_Get_Playlist_List (device);
166 playlists = newplaylists;
167 playlists_changed = FALSE;
168 }
169 }
170
171 int
save_playlist(const char * path,struct fuse_file_info * fi)172 save_playlist (const char *path, struct fuse_file_info *fi)
173 {
174 DBG ("save_playlist");
175 int ret = 0;
176
177 LIBMTP_playlist_t *playlist;
178 FILE *file = NULL;
179 char item_path[1024];
180 uint32_t item_id = 0;
181 uint32_t *tracks;
182 gchar **fields;
183 GSList *tmplist = NULL;
184
185 fields = g_strsplit (path, "/", -1);
186 gchar *playlist_name;
187 playlist_name = g_strndup (fields[2], strlen (fields[2]) - 4);
188 DBG ("Adding:%s", playlist_name);
189 g_strfreev (fields);
190
191 playlist = LIBMTP_new_playlist_t ();
192 playlist->name = g_strdup (playlist_name);
193
194 file = fdopen (fi->fh, "r");
195 while (fgets (item_path, sizeof (item_path) - 1, file) != NULL)
196 {
197 g_strchomp (item_path);
198 item_id = parse_path (item_path);
199 if (item_id != -1)
200 {
201 tmplist = g_slist_append (tmplist, GUINT_TO_POINTER (item_id));
202 DBG ("Adding to tmplist:%d", item_id);
203 }
204 }
205 playlist->no_tracks = g_slist_length (tmplist);
206 tracks = g_malloc (playlist->no_tracks * sizeof (uint32_t));
207 int i;
208 for (i = 0; i < playlist->no_tracks; i++)
209 {
210 tracks[i] = (uint32_t) GPOINTER_TO_UINT (g_slist_nth_data (tmplist, i));
211 DBG ("Adding:%d-%d", i, tracks[i]);
212 }
213 playlist->tracks = tracks;
214 DBG ("Total:%d", playlist->no_tracks);
215
216 int playlist_id = 0;
217 LIBMTP_playlist_t *tmp_playlist;
218 check_playlists ();
219 tmp_playlist = playlists;
220 while (tmp_playlist != NULL)
221 {
222 if (g_ascii_strcasecmp (tmp_playlist->name, playlist_name) == 0)
223 {
224 playlist_id = playlist->playlist_id;
225 }
226 tmp_playlist = tmp_playlist->next;
227 }
228
229 if (playlist_id > 0)
230 {
231 DBG ("Update playlist %d", playlist_id);
232 playlist->playlist_id = playlist_id;
233 ret = LIBMTP_Update_Playlist (device, playlist);
234 }
235 else
236 {
237 DBG ("New playlist");
238 ret = LIBMTP_Create_New_Playlist (device, playlist);
239 }
240 playlists_changed = TRUE;
241 return ret;
242 }
243
244 /* Find the file type based on extension */
245 static LIBMTP_filetype_t
find_filetype(const gchar * filename)246 find_filetype (const gchar * filename)
247 {
248 DBG ("find_filetype");
249 gchar **fields;
250 fields = g_strsplit (filename, ".", -1);
251 gchar *ptype;
252 ptype = g_strdup (fields[g_strv_length (fields) - 1]);
253 g_strfreev (fields);
254 LIBMTP_filetype_t filetype;
255
256 // This need to be kept constantly updated as new file types arrive.
257 if (!g_ascii_strncasecmp (ptype, "wav", 3))
258 {
259 filetype = LIBMTP_FILETYPE_WAV;
260 }
261 else if (!g_ascii_strncasecmp (ptype, "mp3", 3))
262 {
263 filetype = LIBMTP_FILETYPE_MP3;
264 }
265 else if (!g_ascii_strncasecmp (ptype, "wma", 3))
266 {
267 filetype = LIBMTP_FILETYPE_WMA;
268 }
269 else if (!g_ascii_strncasecmp (ptype, "ogg", 3))
270 {
271 filetype = LIBMTP_FILETYPE_OGG;
272 }
273 else if (!g_ascii_strncasecmp (ptype, "aa", 2))
274 {
275 filetype = LIBMTP_FILETYPE_AUDIBLE;
276 }
277 else if (!g_ascii_strncasecmp (ptype, "mp4", 3))
278 {
279 filetype = LIBMTP_FILETYPE_MP4;
280 }
281 else if (!g_ascii_strncasecmp (ptype, "wmv", 3))
282 {
283 filetype = LIBMTP_FILETYPE_WMV;
284 }
285 else if (!g_ascii_strncasecmp (ptype, "avi", 3))
286 {
287 filetype = LIBMTP_FILETYPE_AVI;
288 }
289 else if (!g_ascii_strncasecmp (ptype, "mpeg", 4)
290 || !g_ascii_strncasecmp (ptype, "mpg", 3))
291 {
292 filetype = LIBMTP_FILETYPE_MPEG;
293 }
294 else if (!g_ascii_strncasecmp (ptype, "asf", 3))
295 {
296 filetype = LIBMTP_FILETYPE_ASF;
297 }
298 else if (!g_ascii_strncasecmp (ptype, "qt", 2)
299 || !g_ascii_strncasecmp (ptype, "mov", 3))
300 {
301 filetype = LIBMTP_FILETYPE_QT;
302 }
303 else if (!g_ascii_strncasecmp (ptype, "wma", 3))
304 {
305 filetype = LIBMTP_FILETYPE_WMA;
306 }
307 else if (!g_ascii_strncasecmp (ptype, "jpg", 3)
308 || !g_ascii_strncasecmp (ptype, "jpeg", 4))
309 {
310 filetype = LIBMTP_FILETYPE_JPEG;
311 }
312 else if (!g_ascii_strncasecmp (ptype, "jfif", 4))
313 {
314 filetype = LIBMTP_FILETYPE_JFIF;
315 }
316 else if (!g_ascii_strncasecmp (ptype, "tif", 3)
317 || !g_ascii_strncasecmp (ptype, "tiff", 4))
318 {
319 filetype = LIBMTP_FILETYPE_TIFF;
320 }
321 else if (!g_ascii_strncasecmp (ptype, "bmp", 3))
322 {
323 filetype = LIBMTP_FILETYPE_BMP;
324 }
325 else if (!g_ascii_strncasecmp (ptype, "gif", 3))
326 {
327 filetype = LIBMTP_FILETYPE_GIF;
328 }
329 else if (!g_ascii_strncasecmp (ptype, "pic", 3)
330 || !g_ascii_strncasecmp (ptype, "pict", 4))
331 {
332 filetype = LIBMTP_FILETYPE_PICT;
333 }
334 else if (!g_ascii_strncasecmp (ptype, "png", 3))
335 {
336 filetype = LIBMTP_FILETYPE_PNG;
337 }
338 else if (!g_ascii_strncasecmp (ptype, "wmf", 3))
339 {
340 filetype = LIBMTP_FILETYPE_WINDOWSIMAGEFORMAT;
341 }
342 else if (!g_ascii_strncasecmp (ptype, "ics", 3))
343 {
344 filetype = LIBMTP_FILETYPE_VCALENDAR2;
345 }
346 else if (!g_ascii_strncasecmp (ptype, "exe", 3)
347 || !g_ascii_strncasecmp (ptype, "com", 3)
348 || !g_ascii_strncasecmp (ptype, "bat", 3)
349 || !g_ascii_strncasecmp (ptype, "dll", 3)
350 || !g_ascii_strncasecmp (ptype, "sys", 3))
351 {
352 filetype = LIBMTP_FILETYPE_WINEXEC;
353 }
354 else if (!g_ascii_strncasecmp (ptype, "txt", 3))
355 {
356 filetype = LIBMTP_FILETYPE_TEXT;
357 }
358 else if (!g_ascii_strncasecmp (ptype, "htm", 3)
359 || !g_ascii_strncasecmp (ptype, "html", 4))
360 {
361 filetype = LIBMTP_FILETYPE_HTML;
362 }
363 else if (!g_ascii_strncasecmp (ptype, "bin", 3))
364 {
365 filetype = LIBMTP_FILETYPE_FIRMWARE;
366 }
367 else if (!g_ascii_strncasecmp (ptype, "aac", 3))
368 {
369 filetype = LIBMTP_FILETYPE_AAC;
370 }
371 else if (!g_ascii_strncasecmp (ptype, "flac", 4)
372 || !g_ascii_strncasecmp (ptype, "fla", 3))
373 {
374 filetype = LIBMTP_FILETYPE_FLAC;
375 }
376 else if (!g_ascii_strncasecmp (ptype, "mp2", 3))
377 {
378 filetype = LIBMTP_FILETYPE_MP2;
379 }
380 else if (!g_ascii_strncasecmp (ptype, "m4a", 3))
381 {
382 filetype = LIBMTP_FILETYPE_M4A;
383 }
384 else if (!g_ascii_strncasecmp (ptype, "doc", 3))
385 {
386 filetype = LIBMTP_FILETYPE_DOC;
387 }
388 else if (!g_ascii_strncasecmp (ptype, "xml", 3))
389 {
390 filetype = LIBMTP_FILETYPE_XML;
391 }
392 else if (!g_ascii_strncasecmp (ptype, "xls", 3))
393 {
394 filetype = LIBMTP_FILETYPE_XLS;
395 }
396 else if (!g_ascii_strncasecmp (ptype, "ppt", 3))
397 {
398 filetype = LIBMTP_FILETYPE_PPT;
399 }
400 else if (!g_ascii_strncasecmp (ptype, "mht", 3))
401 {
402 filetype = LIBMTP_FILETYPE_MHT;
403 }
404 else if (!g_ascii_strncasecmp (ptype, "jp2", 3))
405 {
406 filetype = LIBMTP_FILETYPE_JP2;
407 }
408 else if (!g_ascii_strncasecmp (ptype, "jpx", 3))
409 {
410 filetype = LIBMTP_FILETYPE_JPX;
411 }
412 else
413 {
414 DBG ("Sorry, file type \"%s\" is not yet supported", ptype);
415 DBG ("Tagging as unknown file type.");
416 filetype = LIBMTP_FILETYPE_UNKNOWN;
417 }
418 g_free (ptype);
419 return filetype;
420 }
421
422 static int
find_storage(const gchar * path)423 find_storage (const gchar * path)
424 {
425 int i;
426 DBG ("find_storage:%s", path);
427 for (i = 0; i < 4; i++)
428 {
429 if (storageArea[i].storage != NULL)
430 {
431 int maxlen = strlen (storageArea[i].storage->StorageDescription);
432 if (strlen (path + 1) < maxlen)
433 maxlen = strlen (path + 1);
434 if (strncmp
435 (storageArea[i].storage->StorageDescription,
436 path + 1, maxlen) == 0)
437 {
438 DBG ("%s found as %d",
439 storageArea[i].storage->StorageDescription, i);
440 return i;
441 }
442 }
443 }
444 DBG ("could not find storage for %s", path);
445 return -1;
446 }
447
448 static int
lookup_folder_id(LIBMTP_folder_t * folderlist,gchar * path,gchar * parent)449 lookup_folder_id (LIBMTP_folder_t * folderlist, gchar * path, gchar * parent)
450 {
451 DBG ("lookup_folder_id %s,%s", path, parent);
452 int ret = -1;
453 if (folderlist == NULL)
454 {
455 return -1;
456 }
457 gchar *mypath;
458 mypath = path;
459 check_folders ();
460 if (parent == NULL)
461 {
462 if (g_strrstr (path + 1, "/") == NULL)
463 {
464 DBG ("Storage dir");
465 return -2;
466 }
467 else
468 {
469 DBG ("Strip storage area name");
470 mypath = strstr (path + 1, "/");
471 parent = "";
472 }
473 }
474
475 gchar *current;
476 current = g_strconcat (parent, "/", folderlist->name, NULL);
477 LIBMTP_devicestorage_t *storage;
478
479 DBG ("compare %s,%s", mypath, current);
480 if (g_ascii_strcasecmp (mypath, current) == 0)
481 {
482 ret = folderlist->folder_id;
483 }
484 else if (g_ascii_strncasecmp (mypath, current, strlen (current)) == 0)
485 {
486 ret = lookup_folder_id (folderlist->child, mypath, current);
487 }
488
489 if (ret == -1)
490 {
491 ret = lookup_folder_id (folderlist->sibling, mypath, parent);
492 }
493 g_free (current);
494 return ret;
495 }
496
497 static int
parse_path(const gchar * path)498 parse_path (const gchar * path)
499 {
500 DBG ("parse_path:%s", path);
501 int res;
502 int item_id = -1;
503 int i;
504 // Check cached files first
505 GSList *item;
506 item = g_slist_find_custom (myfiles, path, (GCompareFunc) strcmp);
507 if (item != NULL)
508 return 0;
509
510 // Check Playlists
511 if (strncmp ("/Playlists", path, 10) == 0)
512 {
513 LIBMTP_playlist_t *playlist;
514
515 res = -ENOENT;
516 check_playlists ();
517 playlist = playlists;
518 while (playlist != NULL)
519 {
520 gchar *tmppath;
521 tmppath = g_strconcat ("/Playlists/", playlist->name, ".m3u", NULL);
522 if (g_ascii_strcasecmp (path, tmppath) == 0)
523 {
524 res = playlist->playlist_id;
525 g_free (tmppath);
526 break;
527 }
528 g_free (tmppath);
529 playlist = playlist->next;
530 }
531 return res;
532 }
533 // Check lost+found
534 if (strncmp ("/lost+found", path, 11) == 0)
535 {
536 GSList *item;
537 gchar *filename = g_path_get_basename (path);
538
539 res = -ENOENT;
540 for (item = lostfiles; item != NULL; item = g_slist_next (item))
541 {
542 LIBMTP_file_t *file = (LIBMTP_file_t *) item->data;
543
544 if (strcmp (file->filename, filename) == 0)
545 {
546 res = file->item_id;
547 break;
548 }
549 }
550 g_free (filename);
551 return res;
552 }
553 // Check device
554 LIBMTP_folder_t *folder;
555 gchar **fields;
556 gchar *directory;
557 directory = (gchar *) g_malloc (strlen (path) + 1);
558 directory = strcpy (directory, "");
559 fields = g_strsplit (path, "/", -1);
560 res = -ENOENT;
561 int storageid;
562 storageid = find_storage (path);
563 if (storageid < 0)
564 {
565 return res;
566 }
567 for (i = 0; fields[i] != NULL; i++)
568 {
569 if (strlen (fields[i]) > 0)
570 {
571 if (fields[i + 1] != NULL)
572 {
573 directory = strcat (directory, "/");
574 directory = strcat (directory, fields[i]);
575 }
576 else
577 {
578 check_folders ();
579 folder = storageArea[storageid].folders;
580 int folder_id = 0;
581 if (strcmp (directory, "") != 0)
582 {
583 folder_id = lookup_folder_id (folder, directory, NULL);
584 }
585 DBG ("parent id:%d:%s", folder_id, directory);
586 LIBMTP_file_t *file;
587 check_files ();
588 file = files;
589 while (file != NULL)
590 {
591 if ((file->parent_id == folder_id) ||
592 (folder_id == -2
593 && (file->parent_id == 0)
594 && (file->storage_id ==
595 storageArea[storageid].storage->id)))
596 {
597 if (file->filename == NULL)
598 DBG ("MTPFS filename NULL");
599 if (file->filename != NULL
600 &&
601 g_ascii_strcasecmp (file->filename, fields[i]) == 0)
602 {
603 DBG ("found:%d:%s", file->item_id, file->filename);
604
605 item_id = file->item_id;
606 break; // found!
607 }
608 }
609 file = file->next;
610 }
611 if (item_id < 0)
612 {
613 directory = strcat (directory, "/");
614 directory = strcat (directory, fields[i]);
615 item_id = lookup_folder_id (folder, directory, NULL);
616 res = item_id;
617 break;
618 }
619 else
620 {
621 res = item_id;
622 break;
623 }
624 }
625 }
626 }
627 g_free (directory);
628 g_strfreev (fields);
629 DBG ("parse_path exiting:%s - %d", path, res);
630 return res;
631 }
632
633 static int
mtpfs_release(const char * path,struct fuse_file_info * fi)634 mtpfs_release (const char *path, struct fuse_file_info *fi)
635 {
636 enter_lock ("release: %s", path);
637 // Check cached files first
638 GSList *item;
639 item = g_slist_find_custom (myfiles, path, (GCompareFunc) strcmp);
640
641 if (item != NULL)
642 {
643 if (strncmp ("/Playlists/", path, 11) == 0)
644 {
645 save_playlist (path, fi);
646 close (fi->fh);
647 return_unlock (0);
648 }
649 else
650 {
651 //find parent id
652 gchar *filename = g_strdup ("");
653 gchar **fields;
654 gchar *directory;
655 directory = (gchar *) g_malloc (strlen (path) + 1);
656 directory = strcpy (directory, "/");
657 fields = g_strsplit (path, "/", -1);
658 int i;
659 int parent_id = 0;
660 int storageid;
661 storageid = find_storage (path);
662 if (storageid < 0)
663 {
664 return_unlock (-ENOENT);
665 }
666 for (i = 0; fields[i] != NULL; i++)
667 {
668 if (strlen (fields[i]) > 0)
669 {
670 if (fields[i + 1] == NULL)
671 {
672 gchar *tmp = g_strndup (directory,
673 strlen (directory) - 1);
674 parent_id =
675 lookup_folder_id (storageArea
676 [storageid].folders, tmp, NULL);
677 g_free (tmp);
678 if (parent_id < 0)
679 parent_id = 0;
680 g_free (filename);
681 filename = g_strdup (fields[i]);
682 }
683 else
684 {
685 directory = strcat (directory, fields[i]);
686 directory = strcat (directory, "/");
687 }
688 }
689 }
690 DBG ("%s:%s:%d", filename, directory, parent_id);
691
692 struct stat st;
693 uint64_t filesize;
694 fstat (fi->fh, &st);
695 filesize = (uint64_t) st.st_size;
696
697 // Setup file
698 int ret;
699 LIBMTP_filetype_t filetype;
700 filetype = find_filetype (filename);
701 #ifdef USEMAD
702 if (filetype == LIBMTP_FILETYPE_MP3)
703 {
704 LIBMTP_track_t *genfile;
705 genfile = LIBMTP_new_track_t ();
706 gint songlen;
707 struct id3_file *id3_fh;
708 struct id3_tag *tag;
709 gchar *tracknum;
710
711 id3_fh = id3_file_fdopen (fi->fh, ID3_FILE_MODE_READONLY);
712 tag = id3_file_tag (id3_fh);
713
714 genfile->artist = getArtist (tag);
715 genfile->title = getTitle (tag);
716 genfile->album = getAlbum (tag);
717 genfile->genre = getGenre (tag);
718 genfile->date = getYear (tag);
719 genfile->usecount = 0;
720 genfile->parent_id = (uint32_t) parent_id;
721 genfile->storage_id = storageArea[storageid].storage->id;
722
723 /* If there is a songlength tag it will take
724 * precedence over any length calculated from
725 * the bitrate and filesize */
726 songlen = getSonglen (tag);
727 if (songlen > 0)
728 {
729 genfile->duration = songlen * 1000;
730 }
731 else
732 {
733 genfile->duration = (uint16_t) calc_length (fi->fh) * 1000;
734 //genfile->duration = 293000;
735 }
736
737 tracknum = getTracknum (tag);
738 if (tracknum != NULL)
739 {
740 genfile->tracknumber = strtoul (tracknum, NULL, 10);
741 }
742 else
743 {
744 genfile->tracknumber = 0;
745 }
746 g_free (tracknum);
747
748 // Compensate for missing tag information
749 if (!genfile->artist)
750 genfile->artist = g_strdup ("<Unknown>");
751 if (!genfile->title)
752 genfile->title = g_strdup ("<Unknown>");
753 if (!genfile->album)
754 genfile->album = g_strdup ("<Unknown>");
755 if (!genfile->genre)
756 genfile->genre = g_strdup ("<Unknown>");
757
758 genfile->filesize = filesize;
759 genfile->filetype = filetype;
760 genfile->filename = g_strdup (filename);
761 //title,artist,genre,album,date,tracknumber,duration,samplerate,nochannels,wavecodec,bitrate,bitratetype,rating,usecount
762 //DBG("%d:%d:%d",fi->fh,genfile->duration,genfile->filesize);
763 ret =
764 LIBMTP_Send_Track_From_File_Descriptor
765 (device, fi->fh, genfile, NULL, NULL);
766 id3_file_close (id3_fh);
767 LIBMTP_destroy_track_t (genfile);
768 DBG ("Sent TRACK %s", path);
769 }
770 else
771 {
772 #endif
773 LIBMTP_file_t *genfile;
774 genfile = LIBMTP_new_file_t ();
775 genfile->filesize = filesize;
776 genfile->filetype = filetype;
777 genfile->filename = g_strdup (filename);
778 genfile->parent_id = (uint32_t) parent_id;
779 genfile->storage_id = storageArea[storageid].storage->id;
780
781 ret =
782 LIBMTP_Send_File_From_File_Descriptor
783 (device, fi->fh, genfile, NULL, NULL);
784 LIBMTP_destroy_file_t (genfile);
785 DBG ("Sent FILE %s", path);
786 #ifdef USEMAD
787 }
788 #endif
789 if (ret == 0)
790 {
791 DBG ("Sent %s", path);
792 }
793 else
794 {
795 DBG ("Problem sending %s - %d", path, ret);
796 }
797 // Cleanup
798 if (item && item->data)
799 g_free (item->data);
800 myfiles = g_slist_remove (myfiles, item->data);
801 g_strfreev (fields);
802 g_free (filename);
803 g_free (directory);
804 close (fi->fh);
805 // Refresh filelist
806 files_changed = TRUE;
807 return_unlock (ret);
808 }
809 }
810 close (fi->fh);
811 return_unlock (0);
812 }
813
814 void
mtpfs_destroy()815 mtpfs_destroy ()
816 {
817 enter_lock ("destroy");
818 if (files)
819 free_files (files);
820 int i;
821 for (i = 0; i < 4; i++)
822 {
823 if (storageArea[i].folders)
824 LIBMTP_destroy_folder_t (storageArea[i].folders);
825 }
826 if (playlists)
827 free_playlists (playlists);
828 if (device)
829 LIBMTP_Release_Device (device);
830 return_unlock ();
831 }
832
833 static int
mtpfs_readdir(const gchar * path,void * buf,fuse_fill_dir_t filler,off_t offset,struct fuse_file_info * fi)834 mtpfs_readdir (const gchar * path, void *buf, fuse_fill_dir_t filler,
835 off_t offset, struct fuse_file_info *fi)
836 {
837 enter_lock ("readdir %s", path);
838 LIBMTP_folder_t *folder;
839
840 // Add common entries
841 filler (buf, ".", NULL, 0);
842 filler (buf, "..", NULL, 0);
843
844 // If in root directory
845 if (strcmp (path, "/") == 0)
846 {
847 filler (buf, "Playlists", NULL, 0);
848 if (lostfiles != NULL)
849 {
850 filler (buf, "lost+found", NULL, 0);
851 }
852 LIBMTP_devicestorage_t *storage;
853 for (storage = device->storage; storage != 0; storage = storage->next)
854 {
855 struct stat st;
856 memset (&st, 0, sizeof (st));
857 st.st_nlink = 2;
858 st.st_ino = storage->id;
859 st.st_mode = S_IFREG | 0555;
860 gchar *name;
861 filler (buf, storage->StorageDescription, &st, 0);
862 }
863 return_unlock (0);
864 }
865 // Are we looking at the playlists?
866 if (strncmp (path, "/Playlists", 10) == 0)
867 {
868 DBG ("Checking Playlists");
869 LIBMTP_playlist_t *playlist;
870 check_playlists ();
871 playlist = playlists;
872 while (playlist != NULL)
873 {
874 struct stat st;
875 memset (&st, 0, sizeof (st));
876 st.st_ino = playlist->playlist_id;
877 st.st_mode = S_IFREG | 0666;
878 gchar *name;
879 name = g_strconcat (playlist->name, ".m3u", NULL);
880 DBG ("Playlist:%s", name);
881 if (filler (buf, name, &st, 0))
882 {
883 g_free (name);
884 break;
885 }
886 g_free (name);
887 playlist = playlist->next;
888 }
889 return_unlock (0);
890 }
891 // Are we looking at lost+found dir?
892 if (strncmp (path, "/lost+found", 11) == 0)
893 {
894 check_files ();
895 GSList *item;
896
897 for (item = lostfiles; item != NULL; item = g_slist_next (item))
898 {
899 LIBMTP_file_t *file = (LIBMTP_file_t *) item->data;
900
901 struct stat st;
902 memset (&st, 0, sizeof (st));
903 st.st_ino = file->item_id;
904 st.st_mode = S_IFREG | 0444;
905 if (filler
906 (buf,
907 (file->filename ==
908 NULL ? "<mtpfs null>" : file->filename), &st, 0))
909 break;
910 }
911 return_unlock (0);
912 }
913 // Get storage area
914 int i;
915 int storageid = -1;
916 storageid = find_storage (path);
917 if (storageid < 0)
918 {
919 return_unlock (-ENOENT);
920 }
921 // Get folder listing.
922 int folder_id = 0;
923 if (strcmp (path, "/") != 0)
924 {
925 check_folders ();
926 folder_id =
927 lookup_folder_id (storageArea[storageid].folders,
928 (gchar *) path, NULL);
929 }
930
931 DBG ("Checking folders for %d", storageid);
932 check_folders ();
933 if (folder_id == -2)
934 {
935 DBG ("Root of storage area");
936 folder = storageArea[storageid].folders;
937 }
938 else
939 {
940 folder = LIBMTP_Find_Folder (storageArea[storageid].folders, folder_id);
941 if (folder == NULL)
942 return_unlock (0);
943 folder = folder->child;
944 }
945 if (folder == NULL)
946 return_unlock (0);
947
948 while (folder != NULL)
949 {
950 if ((folder->parent_id == folder_id) ||
951 (folder_id == -2
952 && (folder->storage_id == storageArea[storageid].storage->id)))
953 {
954 DBG ("found folder: %s, id %d", folder->name, folder->folder_id);
955 struct stat st;
956 memset (&st, 0, sizeof (st));
957 st.st_ino = folder->folder_id;
958 st.st_mode = S_IFDIR | 0777;
959 if (filler (buf, folder->name, &st, 0))
960 break;
961 }
962 folder = folder->sibling;
963 }
964 DBG ("Checking folders end");
965 LIBMTP_destroy_folder_t (folder);
966 DBG ("Checking files");
967 // Find files
968 LIBMTP_file_t *file, *tmp;
969 check_files ();
970 file = files;
971 while (file != NULL)
972 {
973 if ((file->parent_id == folder_id) ||
974 (folder_id == -2 && (file->parent_id == 0)
975 && (file->storage_id == storageArea[storageid].storage->id)))
976 {
977 struct stat st;
978 memset (&st, 0, sizeof (st));
979 st.st_ino = file->item_id;
980 st.st_mode = S_IFREG | 0444;
981 if (filler
982 (buf,
983 (file->filename ==
984 NULL ? "<mtpfs null>" : file->filename), &st, 0))
985 break;
986 }
987 tmp = file;
988 file = file->next;
989 }
990 DBG ("readdir exit");
991 return_unlock (0);
992 }
993
994 static int
mtpfs_getattr_real(const gchar * path,struct stat * stbuf)995 mtpfs_getattr_real (const gchar * path, struct stat *stbuf)
996 {
997 int ret = 0;
998 if (path == NULL)
999 return -ENOENT;
1000 memset (stbuf, 0, sizeof (struct stat));
1001
1002 // Set uid/gid of file
1003 struct fuse_context *fc;
1004 fc = fuse_get_context ();
1005 stbuf->st_uid = fc->uid;
1006 stbuf->st_gid = fc->gid;
1007 if (strcmp (path, "/") == 0)
1008 {
1009 stbuf->st_mode = S_IFDIR | 0777;
1010 stbuf->st_nlink = 2;
1011 return 0;
1012 }
1013 // Check cached files first (stuff that hasn't been written to dev yet)
1014 GSList *item;
1015 if (myfiles != NULL)
1016 {
1017 item = g_slist_find_custom (myfiles, path, (GCompareFunc) strcmp);
1018 if (item != NULL)
1019 {
1020 stbuf->st_mode = S_IFREG | 0777;
1021 stbuf->st_size = 0;
1022 stbuf->st_blocks = 2;
1023 stbuf->st_mtime = time (NULL);
1024 return 0;
1025 }
1026 }
1027 // Special case directory 'Playlists', 'lost+found'
1028 // Special case root directory items
1029 if (g_strrstr (path + 1, "/") == NULL)
1030 {
1031 stbuf->st_mode = S_IFDIR | 0777;
1032 stbuf->st_nlink = 2;
1033 return 0;
1034 }
1035
1036 int storageid;
1037 storageid = find_storage (path);
1038 if (storageid < 0)
1039 {
1040 return -ENOENT;
1041 }
1042
1043 if (g_ascii_strncasecmp (path, "/Playlists", 10) == 0)
1044 {
1045 LIBMTP_playlist_t *playlist;
1046 check_playlists ();
1047 playlist = playlists;
1048 while (playlist != NULL)
1049 {
1050 gchar *tmppath;
1051 tmppath = g_strconcat ("/Playlists/", playlist->name, ".m3u", NULL);
1052 if (g_ascii_strcasecmp (path, tmppath) == 0)
1053 {
1054 int filesize = 0;
1055 int i;
1056 for (i = 0; i < playlist->no_tracks; i++)
1057 {
1058 LIBMTP_file_t *file;
1059 LIBMTP_folder_t *folder;
1060 file =
1061 LIBMTP_Get_Filemetadata (device, playlist->tracks[i]);
1062 if (file != NULL)
1063 {
1064 int parent_id = file->parent_id;
1065 filesize = filesize + strlen (file->filename) + 2;
1066 while (parent_id != 0)
1067 {
1068 check_folders ();
1069 folder =
1070 LIBMTP_Find_Folder
1071 (storageArea[storageid].folders, parent_id);
1072 if (folder == NULL)
1073 {
1074 DBG ("could not find %d in storage-area %d",
1075 parent_id, storageid);
1076 return -ENOENT;
1077 }
1078 parent_id = folder->parent_id;
1079 filesize = filesize + strlen (folder->name) + 1;
1080 }
1081 }
1082 }
1083 stbuf->st_mode = S_IFREG | 0777;
1084 stbuf->st_size = filesize;
1085 stbuf->st_blocks = 2;
1086 stbuf->st_mtime = time (NULL);
1087 return 0;
1088 }
1089 playlist = playlist->next;
1090 }
1091 return -ENOENT;
1092 }
1093
1094 if (strncasecmp (path, "/lost+found", 11) == 0)
1095 {
1096 GSList *item;
1097 int item_id = parse_path (path);
1098 for (item = lostfiles; item != NULL; item = g_slist_next (item))
1099 {
1100 LIBMTP_file_t *file = (LIBMTP_file_t *) item->data;
1101
1102 if (item_id == file->item_id)
1103 {
1104 stbuf->st_ino = item_id;
1105 stbuf->st_size = file->filesize;
1106 stbuf->st_blocks = (file->filesize / 512) +
1107 (file->filesize % 512 > 0 ? 1 : 0);
1108 stbuf->st_nlink = 1;
1109 stbuf->st_mode = S_IFREG | 0777;
1110 stbuf->st_mtime = file->modificationdate;
1111 return 0;
1112 }
1113 }
1114
1115 return -ENOENT;
1116 }
1117
1118 int item_id = -1;
1119 check_folders ();
1120 item_id =
1121 lookup_folder_id (storageArea[storageid].folders, (gchar *) path, NULL);
1122 if (item_id >= 0)
1123 {
1124 // Must be a folder
1125 stbuf->st_ino = item_id;
1126 stbuf->st_mode = S_IFDIR | 0777;
1127 stbuf->st_nlink = 2;
1128 }
1129 else
1130 {
1131 // Must be a file
1132 item_id = parse_path (path);
1133 LIBMTP_file_t *file;
1134 DBG ("id:path=%d:%s", item_id, path);
1135 check_files ();
1136 file = files;
1137 gboolean found = FALSE;
1138 while (file != NULL)
1139 {
1140 if (file->item_id == item_id)
1141 {
1142 stbuf->st_ino = item_id;
1143 stbuf->st_size = file->filesize;
1144 stbuf->st_blocks = (file->filesize / 512) +
1145 (file->filesize % 512 > 0 ? 1 : 0);
1146 stbuf->st_nlink = 1;
1147 stbuf->st_mode = S_IFREG | 0777;
1148 DBG ("time:%s", ctime (&(file->modificationdate)));
1149 stbuf->st_mtime = file->modificationdate;
1150 stbuf->st_ctime = file->modificationdate;
1151 stbuf->st_atime = file->modificationdate;
1152 found = TRUE;
1153 }
1154 file = file->next;
1155 }
1156 if (!found)
1157 {
1158 ret = -ENOENT;
1159 }
1160 }
1161
1162 return ret;
1163 }
1164
1165 static int
mtpfs_getattr(const gchar * path,struct stat * stbuf)1166 mtpfs_getattr (const gchar * path, struct stat *stbuf)
1167 {
1168 enter_lock ("getattr %s", path);
1169
1170 int ret = mtpfs_getattr_real (path, stbuf);
1171
1172 DBG ("getattr exit");
1173 return_unlock (ret);
1174 }
1175
1176 static int
mtpfs_mknod(const gchar * path,mode_t mode,dev_t dev)1177 mtpfs_mknod (const gchar * path, mode_t mode, dev_t dev)
1178 {
1179 enter_lock ("mknod %s", path);
1180 int item_id = parse_path (path);
1181 if (item_id > 0)
1182 return_unlock (-EEXIST);
1183 myfiles = g_slist_append (myfiles, (gpointer) (g_strdup (path)));
1184 DBG ("NEW FILE");
1185 return_unlock (0);
1186 }
1187
1188 static int
mtpfs_open(const gchar * path,struct fuse_file_info * fi)1189 mtpfs_open (const gchar * path, struct fuse_file_info *fi)
1190 {
1191 enter_lock ("open");
1192 int item_id = -1;
1193 item_id = parse_path (path);
1194 if (item_id < 0)
1195 return_unlock (-ENOENT);
1196
1197 switch (fi->flags & O_ACCMODE)
1198 {
1199 case O_RDONLY:
1200 DBG ("read");
1201 break;
1202 case O_WRONLY:
1203 DBG ("write");
1204 break;
1205 case O_RDWR:
1206 DBG ("rdwrite");
1207 break;
1208 default:
1209 DBG ("unexpected access mode: %d", fi->flags & O_ACCMODE);
1210 }
1211
1212 int storageid;
1213 storageid = find_storage (path);
1214 if (storageid < 0)
1215 {
1216 return_unlock (-ENOENT);
1217 }
1218 FILE *filetmp = tmpfile ();
1219 int tmpfile = fileno (filetmp);
1220 if (tmpfile != -1)
1221 {
1222 if (item_id == 0)
1223 {
1224 fi->fh = tmpfile;
1225 }
1226 else if (strncmp ("/Playlists/", path, 11) == 0)
1227 {
1228 // Is a playlist
1229 gchar **fields;
1230 fields = g_strsplit (path, "/", -1);
1231 gchar *name;
1232 name = g_strndup (fields[2], strlen (fields[2]) - 4);
1233 g_strfreev (fields);
1234 fi->fh = tmpfile;
1235 LIBMTP_playlist_t *playlist;
1236 check_playlists ();
1237 playlist = playlists;
1238 while (playlist != NULL)
1239 {
1240 if (g_ascii_strcasecmp (playlist->name, name) == 0)
1241 {
1242 //int playlist_id=playlist->playlist_id;
1243 int i;
1244 for (i = 0; i < playlist->no_tracks; i++)
1245 {
1246 LIBMTP_file_t *file;
1247 LIBMTP_folder_t *folder;
1248 file =
1249 LIBMTP_Get_Filemetadata (device, playlist->tracks[i]);
1250 if (file != NULL)
1251 {
1252 gchar *path;
1253 path = (gchar *) g_malloc (1024);
1254 path = strcpy (path, "/");
1255 int parent_id = file->parent_id;
1256 while (parent_id != 0)
1257 {
1258 check_folders ();
1259 folder =
1260 LIBMTP_Find_Folder
1261 (storageArea[storageid].folders, parent_id);
1262 path = strcat (path, folder->name);
1263 path = strcat (path, "/");
1264 parent_id = folder->parent_id;
1265 }
1266 path = strcat (path, file->filename);
1267 fprintf (filetmp, "%s\n", path);
1268 DBG ("%s\n", path);
1269 }
1270 }
1271 //LIBMTP_destroy_file_t(file);
1272 fflush (filetmp);
1273 break;
1274 }
1275 playlist = playlist->next;
1276 }
1277 }
1278 else
1279 {
1280 int ret = LIBMTP_Get_File_To_File_Descriptor (device, item_id,
1281 tmpfile,
1282 NULL, NULL);
1283 if (ret == 0)
1284 {
1285 fi->fh = tmpfile;
1286 }
1287 else
1288 {
1289 return_unlock (-ENOENT);
1290 }
1291 }
1292 }
1293 else
1294 {
1295 return_unlock (-ENOENT);
1296 }
1297
1298 return_unlock (0);
1299 }
1300
1301 static int
mtpfs_read(const gchar * path,gchar * buf,size_t size,off_t offset,struct fuse_file_info * fi)1302 mtpfs_read (const gchar * path, gchar * buf, size_t size, off_t offset,
1303 struct fuse_file_info *fi)
1304 {
1305 enter_lock ("read");
1306 int ret;
1307
1308 int item_id = -1;
1309 item_id = parse_path (path);
1310 if (item_id < 0)
1311 return_unlock (-ENOENT);
1312
1313 ret = pread (fi->fh, buf, size, offset);
1314 if (ret == -1)
1315 ret = -errno;
1316
1317 return_unlock (ret);
1318 }
1319
1320 static int
mtpfs_write(const gchar * path,const gchar * buf,size_t size,off_t offset,struct fuse_file_info * fi)1321 mtpfs_write (const gchar * path, const gchar * buf, size_t size, off_t offset,
1322 struct fuse_file_info *fi)
1323 {
1324 enter_lock ("write");
1325 int ret;
1326 if (fi->fh != -1)
1327 {
1328 ret = pwrite (fi->fh, buf, size, offset);
1329 }
1330 else
1331 {
1332 ret = -ENOENT;
1333 }
1334
1335 return_unlock (ret);
1336 }
1337
1338 static int
mtpfs_unlink(const gchar * path)1339 mtpfs_unlink (const gchar * path)
1340 {
1341 enter_lock ("unlink");
1342 int ret = 0;
1343 int item_id = -1;
1344 item_id = parse_path (path);
1345 if (item_id < 0)
1346 return_unlock (-ENOENT);
1347 ret = LIBMTP_Delete_Object (device, item_id);
1348 if (ret != 0)
1349 LIBMTP_Dump_Errorstack (device);
1350 if (strncmp (path, "/Playlists", 10) == 0)
1351 {
1352 playlists_changed = TRUE;
1353 }
1354 else
1355 {
1356 files_changed = TRUE;
1357 }
1358
1359 return_unlock (ret);
1360 }
1361
1362 static int
mtpfs_mkdir_real(const char * path,mode_t mode)1363 mtpfs_mkdir_real (const char *path, mode_t mode)
1364 {
1365 if (g_str_has_prefix (path, "/.Trash") == TRUE)
1366 return_unlock (-EPERM);
1367
1368 int ret = 0;
1369 GSList *item;
1370 item = g_slist_find_custom (myfiles, path, (GCompareFunc) strcmp);
1371 int item_id = parse_path (path);
1372 int storageid = find_storage (path);
1373 if (storageid < 0)
1374 {
1375 return_unlock (-ENOENT);
1376 }
1377 if ((item == NULL) && (item_id < 0))
1378 {
1379 // Split path and find parent_id
1380 gchar *filename = g_strdup ("");
1381 gchar **fields;
1382 gchar *directory;
1383
1384 directory = (gchar *) g_malloc (strlen (path) + 1);
1385 directory = strcpy (directory, "/");
1386 fields = g_strsplit (path, "/", -1);
1387 int i;
1388 uint32_t parent_id = 0;
1389 for (i = 0; fields[i] != NULL; i++)
1390 {
1391 if (strlen (fields[i]) > 0)
1392 {
1393 if (fields[i + 1] == NULL)
1394 {
1395 gchar *tmp = g_strndup (directory,
1396 strlen (directory) - 1);
1397 check_folders ();
1398 parent_id =
1399 lookup_folder_id (storageArea
1400 [storageid].folders, tmp, NULL);
1401 g_free (tmp);
1402 if (parent_id < 0)
1403 parent_id = 0;
1404 g_free (filename);
1405 filename = g_strdup (fields[i]);
1406 }
1407 else
1408 {
1409 directory = strcat (directory, fields[i]);
1410 directory = strcat (directory, "/");
1411 }
1412 }
1413 }
1414 DBG ("%s:%s:%d", filename, directory, parent_id);
1415 ret = LIBMTP_Create_Folder (device, filename, parent_id, 0);
1416 g_strfreev (fields);
1417 g_free (directory);
1418 g_free (filename);
1419 if (ret == 0)
1420 {
1421 ret = -EEXIST;
1422 }
1423 else
1424 {
1425 storageArea[storageid].folders_changed = TRUE;
1426 ret = 0;
1427 }
1428 }
1429 else
1430 {
1431 ret = -EEXIST;
1432 }
1433 return ret;
1434 }
1435
1436 static int
mtpfs_mkdir(const char * path,mode_t mode)1437 mtpfs_mkdir (const char *path, mode_t mode)
1438 {
1439 enter_lock ("mkdir: %s", path);
1440 int ret = mtpfs_mkdir_real (path, mode);
1441
1442 return_unlock (ret);
1443 }
1444
1445 static int
mtpfs_rmdir(const char * path)1446 mtpfs_rmdir (const char *path)
1447 {
1448 enter_lock ("rmdir %s", path);
1449 int ret = 0;
1450 int folder_id = -1;
1451 if (strcmp (path, "/") == 0)
1452 {
1453 return_unlock (0);
1454 }
1455 int storageid = find_storage (path);
1456 if (storageid < 0)
1457 {
1458 return_unlock (-ENOENT);
1459 }
1460 folder_id =
1461 lookup_folder_id (storageArea[storageid].folders, (gchar *) path, NULL);
1462 if (folder_id < 0)
1463 return_unlock (-ENOENT);
1464
1465 LIBMTP_Delete_Object (device, folder_id);
1466
1467 storageArea[storageid].folders_changed = TRUE;
1468 return_unlock (ret);
1469 }
1470
1471 /* Not working. need some way in libmtp to rename objects
1472 int
1473 mtpfs_rename (const char *oldname, const char *newname)
1474 {
1475 uint32_t old_id = parse_path(oldname);
1476 LIBMTP_track_t *track;
1477 track = LIBMTP_Get_Trackmetadata(device,old_id);
1478 gchar *filename;
1479 gchar **fields;
1480 gchar *directory;
1481 directory = (gchar *) g_malloc (strlen (newname) + 1);
1482 directory = strcpy (directory, "/");
1483 fields = g_strsplit (newname, "/", -1);
1484 int i;
1485 uint32_t parent_id = 0;
1486 for (i = 0; fields[i] != NULL; i++) {
1487 if (strlen (fields[i]) > 0) {
1488 if (fields[i + 1] == NULL) {
1489 directory = g_strndup (directory, strlen (directory) - 1);
1490 parent_id = lookup_folder_id (folders, directory, NULL);
1491 if (parent_id < 0)
1492 parent_id = 0;
1493 filename = g_strdup (fields[i]);
1494 } else {
1495 directory = strcat (directory, fields[i]);
1496 directory = strcat (directory, "/");
1497
1498 }
1499 }
1500 }
1501 DBG("%s:%s:%d", filename, directory, parent_id);
1502
1503 track->parent_id = parent_id;
1504 track->title = g_strdup(filename);
1505 int ret = LIBMTP_Update_Track_Metadata(device, track);
1506 return ret;
1507 }
1508 */
1509
1510 /* Allow renaming of empty folders only */
1511 int
mtpfs_rename(const char * oldname,const char * newname)1512 mtpfs_rename (const char *oldname, const char *newname)
1513 {
1514 enter_lock ("rename '%s' to '%s'", oldname, newname);
1515
1516 int folder_id = -1, parent_id;
1517 int folder_empty = 1;
1518 int ret = -ENOTEMPTY;
1519 LIBMTP_folder_t *folder;
1520 LIBMTP_file_t *file;
1521
1522 int storageid_old = find_storage (oldname);
1523 if (storageid_old < 0)
1524 {
1525 return_unlock (-ENOENT);
1526 }
1527 int storageid_new = find_storage (newname);
1528 if (storageid_new < 0)
1529 {
1530 return_unlock (-ENOENT);
1531 }
1532 if (strcmp (oldname, "/") != 0)
1533 {
1534 folder_id =
1535 lookup_folder_id (storageArea[storageid_old].folders,
1536 (gchar *) oldname, NULL);
1537 }
1538 if (folder_id < 0)
1539 return_unlock (-ENOENT);
1540
1541 check_folders ();
1542 folder = LIBMTP_Find_Folder (storageArea[storageid_old].folders, folder_id);
1543
1544 /* MTP Folder object not found? */
1545 if (folder == NULL)
1546 return_unlock (-ENOENT);
1547
1548 parent_id = folder->parent_id;
1549 folder = folder->child;
1550
1551 /* Check if empty folder */
1552 DBG ("Checking empty folder start for: subfolders");
1553
1554 while (folder != NULL)
1555 {
1556 if (folder->parent_id == folder_id)
1557 {
1558 folder_empty = 0;
1559 break;
1560 }
1561 folder = folder->sibling;
1562 }
1563
1564 DBG ("Checking empty folder end for: subfolders. Result: %s",
1565 (folder_empty == 1 ? "empty" : "not empty"));
1566
1567 if (folder_empty == 1)
1568 {
1569 /* Find files */
1570 check_files ();
1571 DBG ("Checking empty folder start for: files");
1572 file = files;
1573 while (file != NULL)
1574 {
1575 if (file->parent_id == folder_id)
1576 {
1577 folder_empty = 0;
1578 break;
1579 }
1580 file = file->next;
1581 }
1582 DBG ("Checking empty folder end for: files. Result: %s",
1583 (folder_empty == 1 ? "empty" : "not empty"));
1584
1585 /* Rename folder. First remove old folder, then create the new one */
1586 if (folder_empty == 1)
1587 {
1588 struct stat stbuf;
1589 if ((ret = mtpfs_getattr_real (oldname, &stbuf)) == 0)
1590 {
1591 DBG ("removing folder %s, id %d", oldname, folder_id);
1592
1593 ret = mtpfs_mkdir_real (newname, stbuf.st_mode);
1594 LIBMTP_Delete_Object (device, folder_id);
1595 storageArea[storageid_old].folders_changed = TRUE;
1596 storageArea[storageid_new].folders_changed = TRUE;
1597 }
1598 }
1599 }
1600 return_unlock (ret);
1601 }
1602
1603 static int
mtpfs_statfs(const char * path,struct statfs * stbuf)1604 mtpfs_statfs (const char *path, struct statfs *stbuf)
1605 {
1606 DBG ("mtpfs_statfs");
1607 stbuf->f_bsize = 1024;
1608 stbuf->f_blocks = device->storage->MaxCapacity / 1024;
1609 stbuf->f_bfree = device->storage->FreeSpaceInBytes / 1024;
1610 stbuf->f_ffree = device->storage->FreeSpaceInObjects / 1024;
1611 stbuf->f_bavail = stbuf->f_bfree;
1612 return 0;
1613 }
1614
1615 void *
mtpfs_init()1616 mtpfs_init ()
1617 {
1618 LIBMTP_devicestorage_t *storage;
1619 DBG ("mtpfs_init");
1620 files_changed = TRUE;
1621 playlists_changed = TRUE;
1622 DBG ("Ready");
1623 return 0;
1624 }
1625
1626 int
mtpfs_blank()1627 mtpfs_blank ()
1628 {
1629 // Do nothing
1630 }
1631
1632 static struct fuse_operations mtpfs_oper = {
1633 .chmod = mtpfs_blank,
1634 .release = mtpfs_release,
1635 .readdir = mtpfs_readdir,
1636 .getattr = mtpfs_getattr,
1637 .open = mtpfs_open,
1638 .mknod = mtpfs_mknod,
1639 .read = mtpfs_read,
1640 .write = mtpfs_write,
1641 .unlink = mtpfs_unlink,
1642 .destroy = mtpfs_destroy,
1643 .mkdir = mtpfs_mkdir,
1644 .rmdir = mtpfs_rmdir,
1645 .rename = mtpfs_rename,
1646 .statfs = mtpfs_statfs,
1647 .init = mtpfs_init,
1648 };
1649
1650 int
main(int argc,char * argv[])1651 main (int argc, char *argv[])
1652 {
1653 int fuse_stat;
1654 umask (0);
1655 LIBMTP_raw_device_t *rawdevices;
1656 int numrawdevices;
1657 LIBMTP_error_number_t err;
1658 int device_number = 0;
1659
1660 int opt;
1661 extern int optind;
1662 extern char *optarg;
1663
1664 //while ((opt = getopt(argc, argv, "d")) != -1 ) {
1665 //switch (opt) {
1666 //case 'd':
1667 ////LIBMTP_Set_Debug(9);
1668 //break;
1669 //}
1670 //}
1671
1672 //argc -= optind;
1673 //argv += optind;
1674
1675 LIBMTP_Init ();
1676
1677 fprintf (stdout, "Listing raw device(s)\n");
1678 err = LIBMTP_Detect_Raw_Devices (&rawdevices, &numrawdevices);
1679 switch (err)
1680 {
1681 case LIBMTP_ERROR_NO_DEVICE_ATTACHED:
1682 fprintf (stdout, " No raw devices found.\n");
1683 return 0;
1684 case LIBMTP_ERROR_CONNECTING:
1685 fprintf (stderr,
1686 "Detect: There has been an error connecting. Exiting\n");
1687 return 1;
1688 case LIBMTP_ERROR_MEMORY_ALLOCATION:
1689 fprintf (stderr,
1690 "Detect: Encountered a Memory Allocation Error. Exiting\n");
1691 return 1;
1692 case LIBMTP_ERROR_NONE:
1693 {
1694 int i;
1695
1696 fprintf (stdout, " Found %d device(s):\n", numrawdevices);
1697 for (i = 0; i < numrawdevices; i++)
1698 {
1699 if (rawdevices[i].device_entry.vendor != NULL ||
1700 rawdevices[i].device_entry.product != NULL)
1701 {
1702 fprintf (stdout,
1703 " %s: %s (%04x:%04x) @ bus %d, dev %d\n",
1704 rawdevices[i].device_entry.vendor,
1705 rawdevices[i].device_entry.product,
1706 rawdevices[i].device_entry.vendor_id,
1707 rawdevices[i].device_entry.product_id,
1708 rawdevices[i].bus_location, rawdevices[i].devnum);
1709 }
1710 else
1711 {
1712 fprintf (stdout,
1713 " %04x:%04x @ bus %d, dev %d\n",
1714 rawdevices[i].device_entry.vendor_id,
1715 rawdevices[i].device_entry.product_id,
1716 rawdevices[i].bus_location, rawdevices[i].devnum);
1717 }
1718 }
1719 }
1720 break;
1721 case LIBMTP_ERROR_GENERAL:
1722 default:
1723 fprintf (stderr, "Unknown connection error.\n");
1724 return 1;
1725 }
1726
1727 fprintf (stdout, "Attempting to connect device\n");
1728 device = LIBMTP_Open_Raw_Device (&rawdevices[device_number]);
1729 if (device == NULL)
1730 {
1731 fprintf (stderr, "Unable to open raw device %d\n", device_number);
1732 return 1;
1733 }
1734
1735 LIBMTP_Dump_Errorstack (device);
1736 LIBMTP_Clear_Errorstack (device);
1737
1738 char *friendlyname;
1739 /* Echo the friendly name so we know which device we are working with */
1740 friendlyname = LIBMTP_Get_Friendlyname (device);
1741 if (friendlyname == NULL)
1742 {
1743 printf ("Listing File Information on Device with name: (NULL)\n");
1744 }
1745 else
1746 {
1747 printf ("Listing File Information on Device with name: %s\n",
1748 friendlyname);
1749 g_free (friendlyname);
1750 }
1751
1752 /* Get all storages for this device */
1753 int ret = LIBMTP_Get_Storage (device, LIBMTP_STORAGE_SORTBY_NOTSORTED);
1754 if (ret != 0)
1755 {
1756 fprintf (stdout, "LIBMTP_Get_Storage() failed:%d\n", ret);
1757 LIBMTP_Dump_Errorstack (device);
1758 LIBMTP_Clear_Errorstack (device);
1759 return 1;
1760 }
1761
1762 /* Check if multiple storage areas */
1763 LIBMTP_devicestorage_t *storage;
1764 int i;
1765 for (storage = device->storage, i = 0; storage != 0;
1766 storage = storage->next, i++)
1767 {
1768 storageArea[i].storage = storage;
1769 storageArea[i].folders = NULL;
1770 storageArea[i].folders_changed = TRUE;
1771 DBG ("Storage%d: %d - %s\n", i, storage->id,
1772 storage->StorageDescription);
1773 }
1774
1775 DBG ("Start fuse");
1776
1777 fuse_stat = fuse_main (argc, argv, &mtpfs_oper, NULL);
1778 DBG ("fuse_main returned %d\n", fuse_stat);
1779 return fuse_stat;
1780 }
1781
1782 #ifdef USEMAD
1783 /* Private buffer for passing around with libmad */
1784 typedef struct
1785 {
1786 /* The buffer of raw mpeg data for libmad to decode */
1787 void *buf;
1788
1789 /* Cached data: pointers to the dividing points of frames
1790 in buf, and the playing time at each of those frames */
1791 void **frames;
1792 mad_timer_t *times;
1793
1794 /* fd is the file descriptor if over the network, or -1 if
1795 using mmap()ed files */
1796 int fd;
1797
1798 /* length of the current stream, corrected for id3 tags */
1799 ssize_t length;
1800
1801 /* have we finished fetching this file? (only in non-mmap()'ed case */
1802 int done;
1803
1804 /* total number of frames */
1805 unsigned long num_frames;
1806
1807 /* number of frames to play */
1808 unsigned long max_frames;
1809
1810 /* total duration of the file */
1811 mad_timer_t duration;
1812
1813 /* filename as mpg321 has opened it */
1814 char filename[PATH_MAX];
1815 } buffer;
1816
1817 /* XING parsing is from the MAD winamp input plugin */
1818
1819 struct xing
1820 {
1821 int flags;
1822 unsigned long frames;
1823 unsigned long bytes;
1824 unsigned char toc[100];
1825 long scale;
1826 };
1827
1828 enum
1829 {
1830 XING_FRAMES = 0x0001,
1831 XING_BYTES = 0x0002,
1832 XING_TOC = 0x0004,
1833 XING_SCALE = 0x0008
1834 };
1835
1836 #define XING_MAGIC (('X' << 24) | ('i' << 16) | ('n' << 8) | 'g')
1837
1838 /* Following two function are adapted from mad_timer, from the
1839 libmad distribution */
1840 static int
parse_xing(struct xing * xing,struct mad_bitptr ptr,unsigned int bitlen)1841 parse_xing (struct xing *xing, struct mad_bitptr ptr, unsigned int bitlen)
1842 {
1843 if (bitlen < 64 || mad_bit_read (&ptr, 32) != XING_MAGIC)
1844 goto fail;
1845
1846 xing->flags = mad_bit_read (&ptr, 32);
1847 bitlen -= 64;
1848
1849 if (xing->flags & XING_FRAMES)
1850 {
1851 if (bitlen < 32)
1852 goto fail;
1853
1854 xing->frames = mad_bit_read (&ptr, 32);
1855 bitlen -= 32;
1856 }
1857
1858 if (xing->flags & XING_BYTES)
1859 {
1860 if (bitlen < 32)
1861 goto fail;
1862
1863 xing->bytes = mad_bit_read (&ptr, 32);
1864 bitlen -= 32;
1865 }
1866
1867 if (xing->flags & XING_TOC)
1868 {
1869 int i;
1870
1871 if (bitlen < 800)
1872 goto fail;
1873
1874 for (i = 0; i < 100; ++i)
1875 xing->toc[i] = mad_bit_read (&ptr, 8);
1876
1877 bitlen -= 800;
1878 }
1879
1880 if (xing->flags & XING_SCALE)
1881 {
1882 if (bitlen < 32)
1883 goto fail;
1884
1885 xing->scale = mad_bit_read (&ptr, 32);
1886 bitlen -= 32;
1887 }
1888
1889 return 1;
1890 fail:
1891 xing->flags = 0;
1892 return 0;
1893 }
1894
1895 int
scan(void const * ptr,ssize_t len)1896 scan (void const *ptr, ssize_t len)
1897 {
1898 int duration = 0;
1899 struct mad_stream stream;
1900 struct mad_header header;
1901 struct xing xing;
1902 xing.frames = 0;
1903
1904 unsigned long bitrate = 0;
1905 int has_xing = 0;
1906 int is_vbr = 0;
1907 mad_stream_init (&stream);
1908 mad_header_init (&header);
1909
1910 mad_stream_buffer (&stream, ptr, len);
1911
1912 int num_frames = 0;
1913
1914 /* There are three ways of calculating the length of an mp3:
1915 1) Constant bitrate: One frame can provide the information
1916 needed: # of frames and duration. Just see how long it
1917 is and do the division.
1918 2) Variable bitrate: Xing tag. It provides the number of
1919 frames. Each frame has the same number of samples, so
1920 just use that.
1921 3) All: Count up the frames and duration of each frames
1922 by decoding each one. We do this if we've no other
1923 choice, i.e. if it's a VBR file with no Xing tag.
1924 */
1925
1926 while (1)
1927 {
1928 if (mad_header_decode (&header, &stream) == -1)
1929 {
1930 if (MAD_RECOVERABLE (stream.error))
1931 continue;
1932 else
1933 break;
1934 }
1935
1936 /* Limit xing testing to the first frame header */
1937 if (!num_frames++)
1938 {
1939 if (parse_xing (&xing, stream.anc_ptr, stream.anc_bitlen))
1940 {
1941 is_vbr = 1;
1942
1943 if (xing.flags & XING_FRAMES)
1944 {
1945 /* We use the Xing tag only for frames. If it doesn't have that
1946 information, it's useless to us and we have to treat it as a
1947 normal VBR file */
1948 has_xing = 1;
1949 num_frames = xing.frames;
1950 break;
1951 }
1952 }
1953 }
1954
1955 /* Test the first n frames to see if this is a VBR file */
1956 if (!is_vbr && !(num_frames > 20))
1957 {
1958 if (bitrate && header.bitrate != bitrate)
1959 {
1960 is_vbr = 1;
1961 }
1962
1963 else
1964 {
1965 bitrate = header.bitrate;
1966 }
1967 }
1968
1969 /* We have to assume it's not a VBR file if it hasn't already been
1970 marked as one and we've checked n frames for different bitrates */
1971 else if (!is_vbr)
1972 {
1973 break;
1974 }
1975
1976 duration = header.duration.seconds;
1977 }
1978
1979 if (!is_vbr)
1980 {
1981 double time = (len * 8.0) / (header.bitrate); /* time in seconds */
1982 //double timefrac = (double)time - ((long)(time));
1983 long nsamples = 32 * MAD_NSBSAMPLES (&header); /* samples per frame */
1984
1985 /* samplerate is a constant */
1986 num_frames = (long) (time * header.samplerate / nsamples);
1987
1988 duration = (int) time;
1989 DBG ("d:%d", duration);
1990 }
1991
1992 else if (has_xing)
1993 {
1994 /* modify header.duration since we don't need it anymore */
1995 mad_timer_multiply (&header.duration, num_frames);
1996 duration = header.duration.seconds;
1997 }
1998
1999 else
2000 {
2001 /* the durations have been added up, and the number of frames
2002 counted. We do nothing here. */
2003 }
2004
2005 mad_header_finish (&header);
2006 mad_stream_finish (&stream);
2007 return duration;
2008 }
2009
2010 int
calc_length(int f)2011 calc_length (int f)
2012 {
2013 struct stat filestat;
2014 void *fdm;
2015 char buffer[3];
2016
2017 fstat (f, &filestat);
2018
2019 /* TAG checking is adapted from XMMS */
2020 int length = filestat.st_size;
2021
2022 if (lseek (f, -128, SEEK_END) < 0)
2023 {
2024 /* File must be very short or empty. Forget it. */
2025 return -1;
2026 }
2027
2028 if (read (f, buffer, 3) != 3)
2029 {
2030 return -1;
2031 }
2032
2033 if (!strncmp (buffer, "TAG", 3))
2034 {
2035 length -= 128; /* Correct for id3 tags */
2036 }
2037
2038 fdm = mmap (0, length, PROT_READ, MAP_SHARED, f, 0);
2039 if (fdm == MAP_FAILED)
2040 {
2041 g_error ("Map failed");
2042 return -1;
2043 }
2044
2045 /* Scan the file for a XING header, or calculate the length,
2046 or just scan the whole file and add everything up. */
2047 int duration = scan (fdm, length);
2048
2049 if (munmap (fdm, length) == -1)
2050 {
2051 g_error ("Unmap failed");
2052 return -1;
2053 }
2054
2055 lseek (f, 0, SEEK_SET);
2056 return duration;
2057 }
2058
2059 #endif
2060