/* * Portable Media Player VFS module for coping with Sony NW devices. * * URI Structure: * mple://// * * will have to be uniquefied if there's more than one; * udev already appears to do this for us. * will also have to be uniquefied. * * I had hoped to have a "displayname"/"internal name" distinction, * but gnome_vfs doesn't appear to support that. * * There is a major bug that I can't find in this code: if you delete * a directory, Gnome apparently doesn't believe you, despite the fact * that I appear to be returning the correct error codes, etc. This * manifests through an infinite loop when you try to open up another * directory. */ /* * sources of help and sample code: * http://developer.gnome.org/doc/API/2.0/gnome-vfs-2.0/gnome-vfs-writing-modules.html * http://developer.imendio.com/wiki/Writing_GnomeVFS_Modules * * from the latter, chunks of this code are Copyright (C) 2003 Imendio HB * and used under the provisions of the GPL, which means this code is * GPL also. */ /* * to-do list * * mple layer * * access-by-filename * - retcons the relevant data structures as needed * * new/rename-by-unicode * * vfs layer * * move files between folders - seems to work with read/write? * * distinguish identically-named folders * * distinguish identically-named files * * improve file_info * * leak hunting * * clean out dead code, consolidate live code * */ #include /* kludgerific */ #define _LARGEFILE64_SOURCE 1 #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include G_LOCK_DEFINE_STATIC( musicroot ); static GNode *musicroot = NULL; typedef enum _mnodetype { MNODE_ROOT, MNODE_DEVICE, MNODE_ALBUM, MNODE_TRACK, MAX_MNODE_TYPE, } mnodetype; typedef struct _mnode { GNode *gnode; gchar *displayname; guint32 idx; mnodetype type; void *data; time_t stattime; /* last time we actually checked the physical device */ } mnode; typedef struct { GnomeVFSFileInfoOptions options; GNode *gnode; GNode *current_child; } DirHandle; #ifdef DEBUG /* debug function */ static int indent = 0; static void dump_tree( GNode *node ) { if ( node == NULL ) { indent --; return; } if ( node == musicroot ) { indent = 0; } while( node != NULL ) { int i; for ( i = 0; i < indent; i++ ) { fprintf( stderr, " " ); } if ( node->data ) { fprintf( stderr, "%s (%d)\n", ((mnode *)node->data)->displayname, ((mnode *)node->data)->idx ); } if ( node->children ) { indent++; dump_tree( node->children ); } node = node->next; } } static void dump_node( mnode *mn ) { fprintf( stderr, "node name: %s\n", mn->displayname ); fprintf( stderr, "index: %d\n", mn->idx ); fprintf( stderr, "type: " ); switch( mn->type ) { case MNODE_ROOT: fprintf( stderr, "root" ); break; case MNODE_DEVICE: fprintf( stderr, "device" ); break; case MNODE_ALBUM: fprintf( stderr, "album" ); break; case MNODE_TRACK: fprintf( stderr, "track" ); break; default: fprintf( stderr, "INVALID (%d)", mn->type ); } fprintf( stderr, "\n" ); } #else #define dump_tree( x ) #define dump_node( x ) #endif /* * create a new node with displayname NAME and index IDX */ static mnode *mnode_new( guint32 idx, const gchar *name ) { mnode *m = g_malloc0( sizeof( mnode )); m->displayname = g_strdup( name ); m->idx = idx; /* defaults */ m->type = MNODE_TRACK; m->data = NULL; return m; } /* * cleanup function */ static gboolean mnode_free_func( GNode *gnode, gpointer data ) { mnode *node = gnode->data; g_free( node->displayname ); if ( node->data != NULL ) { g_free( node->data ); } g_free( node ); return FALSE; } /* * rescan a node. This gets called from within mnode_lookup, where * musicroot is already locked, so we can happily add things to the * tree without further locking. */ void rescan_node( GNode *node ) { mnode *mn = node->data, *newnode; mple_device *dev; mple_folder *fptr; mple_track *tptr; guint16 idx = 0; if ( mn == NULL || mn->data == NULL /* shouldn't happen */ ) { return; } switch( mn->type ) { case MNODE_DEVICE: dev = mn->data; if ( mple_parse_pblist( dev ) != NULL ) { while(( fptr = g_list_nth_data( dev->folderlist, idx++ )) != NULL ) { gchar *fname = utf8_from_utf16be((char *)&(fptr->folderdata.foldername), 126 ); if ( strlen( fname ) == 0 ) { fname = g_strdup( "(Unnamed)" ); } /* xxx dupe check */ newnode = mnode_new( idx, fname ); newnode->gnode = g_node_append_data( mn->gnode, newnode ); newnode->type = MNODE_ALBUM; newnode->data = fptr; g_free( fname ); } } break; case MNODE_ALBUM: dev = ((mple_folder *)mn->data)->dev; fptr = g_list_nth_data( dev->folderlist, mn->idx - 1 ); if ( fptr == NULL ) { /* shouldn't happen */ return; } while (( tptr = g_list_nth_data( fptr->tracklist, idx++ ))) { gchar *fname = utf8_from_utf16be( (char *)&(tptr->trackdata.filename ), 127 ); if ( strlen( fname ) == 0 ) { fname = g_strdup( "(Unnamed)" ); } /* xxx dupe check */ newnode = mnode_new( tptr->tracknum, fname ); newnode->gnode = g_node_append_data( mn->gnode, newnode ); newnode->type = MNODE_TRACK; newnode->data = tptr; g_free( fname ); } break; default: /* we don't actually rescan anything else */ break; } } /* * find a node by URI. */ static mnode *mnode_lookup( GnomeVFSURI *uri ) { GNode *node; mnode *mn = NULL; const gchar *path; gchar **bits = NULL, *upath = NULL; guint32 idx = 1; path = gnome_vfs_uri_get_path( uri ); if ( path != NULL ) { upath = gnome_vfs_unescape_string( path, NULL ); } else { upath = g_strdup( "/" ); } G_LOCK( musicroot ); if ( musicroot == NULL ) { /* can't happen! */ goto out; } if ( strcmp( upath, "/" ) == 0 ) { mn = musicroot->data; goto out; } node = musicroot->children; bits = g_strsplit( upath, "/", 0 ); while( node != NULL ) { mn = node->data; /* do a quick sanity check */ switch( mn->type ) { case MNODE_DEVICE: case MNODE_ALBUM: if ( node->children == NULL ) { rescan_node( node ); } break; default: /* skip */ break; } if ( mn == NULL ) { /* can't happen... */ continue; } if ( strcmp( bits[ idx ], mn->displayname ) == 0 ) { idx++; if ( bits[ idx ] == NULL ) { /* yay, found */ goto out; } else { node = node->children; continue; } } node = node->next; } mn = NULL; out: if ( bits != NULL ) { g_strfreev( bits ); } g_free( upath ); G_UNLOCK( musicroot ); return mn; } /* * build the root device, which is a folder full of detected music * players. */ static void init_root( void ) { GList *players = NULL; mnode *mnode; guint32 idx; mnode = mnode_new( 0, "Music Players" ); musicroot = g_node_new( mnode ); mnode->gnode = musicroot; mnode->type = MNODE_ROOT; mnode->stattime = time( NULL ); /* now get the players */ players = mple_get_nw_devs( NULL ); mnode->data = players; if ( g_list_length( players ) == 0 ) { /* no players... pop a dialog? fixme */ } for ( idx = 0; g_list_nth( players, idx ) != NULL; idx++ ) { mple_device *dev = g_list_nth_data( players, idx ); GNode *gnode; if ( dev->path != NULL ) { mnode = mnode_new( idx, g_basename( dev->path )); gnode = g_node_append_data( musicroot, mnode ); mnode->gnode = gnode; mnode->type = MNODE_DEVICE; mnode->data = dev; } else { /* unmounted devices */ } } } /* * clean up the root node */ static void free_root( void ) { g_node_traverse( musicroot, G_PRE_ORDER, G_TRAVERSE_ALL, -1, mnode_free_func, NULL ); g_node_destroy( musicroot ); musicroot = NULL; } static GnomeVFSResult do_open( GnomeVFSMethod *method, GnomeVFSMethodHandle **method_handle, GnomeVFSURI *uri, GnomeVFSOpenMode mode, GnomeVFSContext *context ) { mnode *file, *pfile; MPLEFILE *handle; GnomeVFSURI *parent_uri = NULL; mple_track *tptr; mple_device *device; mple_folder *fptr; guint16 folder, track; const gchar *path = NULL, *upath = NULL, *filename = NULL; file = mnode_lookup( uri ); /* we can only do open() on tracks */ if ( file && file->type != MNODE_TRACK ) { return GNOME_VFS_ERROR_IS_DIRECTORY; } else { /* if we can't find the file and we're not writing... */ if ( file == NULL ) { if (!( mode & GNOME_VFS_OPEN_WRITE )) { return GNOME_VFS_ERROR_NOT_FOUND; } } } /* get the device structure from the parent */ parent_uri = gnome_vfs_uri_get_parent( uri ); pfile = mnode_lookup( parent_uri ); if ( pfile == NULL || pfile->type != MNODE_ALBUM ) { /* ok, I have no idea WHAT you're doing */ return GNOME_VFS_ERROR_NOT_PERMITTED; } fptr = pfile->data; device = fptr->dev; folder = pfile->idx; if ( file != NULL ) { tptr = file->data; track = tptr->tracknum; } else { track = 0; /* next available tracknum */ } if ( mode & GNOME_VFS_OPEN_WRITE ) { /* seriously ugly */ path = gnome_vfs_uri_get_path( uri ); if ( path != NULL ) { upath = gnome_vfs_unescape_string( path, NULL ); } else { /* it's busted */ return GNOME_VFS_ERROR_INTERNAL; } filename = g_basename( upath ); handle = mple_open( device, folder, track, filename, "wb" ); } else { handle = mple_open( device, folder, track, "", "rb" ); } if ( handle != NULL ) { *method_handle = (GnomeVFSMethodHandle *) handle; return GNOME_VFS_OK; } else { return gnome_vfs_result_from_errno(); } } static GnomeVFSResult do_create( GnomeVFSMethod *method, GnomeVFSMethodHandle **method_handle, GnomeVFSURI *uri, GnomeVFSOpenMode mode, gboolean exclusive, guint perm, GnomeVFSContext *context ) { /* We cheat here and don't take perm or exclusive in consideration. */ return do_open( method, method_handle, uri, mode, context ); } static GnomeVFSResult do_read( GnomeVFSMethod *method, GnomeVFSMethodHandle *method_handle, gpointer buffer, GnomeVFSFileSize bytes, GnomeVFSFileSize *bytes_read, GnomeVFSContext *context ) { MPLEFILE *handle = (MPLEFILE *)method_handle; *bytes_read = mple_read( buffer, 1, bytes, handle ); if ( *bytes_read >= 0 ) { return GNOME_VFS_OK; } else { perror( "mple_read" ); return GNOME_VFS_ERROR_INTERNAL; } } static GnomeVFSResult do_seek( GnomeVFSMethod *method, GnomeVFSMethodHandle *method_handle, GnomeVFSSeekPosition whence, GnomeVFSFileOffset offset, GnomeVFSContext *context ) { MPLEFILE *handle = (MPLEFILE *)method_handle; int fwhence; switch ( whence ) { case GNOME_VFS_SEEK_START: fwhence = SEEK_SET; break; case GNOME_VFS_SEEK_CURRENT: fwhence = SEEK_CUR; break; case GNOME_VFS_SEEK_END: fwhence = SEEK_END; break; default: return GNOME_VFS_ERROR_BAD_PARAMETERS; } if ( mple_seek( handle, offset, fwhence ) != 0 ) { return gnome_vfs_result_from_errno(); } return GNOME_VFS_OK; } static GnomeVFSResult do_tell( GnomeVFSMethod *method, GnomeVFSMethodHandle *method_handle, GnomeVFSFileOffset *offset_return ) { MPLEFILE *handle = (MPLEFILE *)method_handle; *offset_return = mple_tell( handle ); return gnome_vfs_result_from_errno(); } static GnomeVFSResult do_write( GnomeVFSMethod *method, GnomeVFSMethodHandle *method_handle, gconstpointer buffer, GnomeVFSFileSize bytes, GnomeVFSFileSize *bytes_written, GnomeVFSContext *context) { MPLEFILE *handle = (MPLEFILE *) method_handle; *bytes_written = mple_write( (void *)buffer, 1, bytes, handle ); return GNOME_VFS_OK; } static GnomeVFSResult do_close( GnomeVFSMethod *method, GnomeVFSMethodHandle *method_handle, GnomeVFSContext *context ) { MPLEFILE *handle = (MPLEFILE *) method_handle; if ( handle != NULL ) { mple_close( handle ); } return GNOME_VFS_OK; } static GnomeVFSResult do_open_directory( GnomeVFSMethod *method, GnomeVFSMethodHandle **method_handle, GnomeVFSURI *uri, GnomeVFSFileInfoOptions options, GnomeVFSContext *context ) { DirHandle *handle; mnode *m; /* this will populate anything on the path that's not already been populated */ m = mnode_lookup( uri ); if ( m == NULL ) { return GNOME_VFS_ERROR_NOT_FOUND; } handle = g_malloc0( sizeof( DirHandle )); handle->gnode = m->gnode; handle->current_child = handle->gnode->children; *method_handle = (GnomeVFSMethodHandle *) handle; return GNOME_VFS_OK; } static GnomeVFSResult do_read_directory( GnomeVFSMethod *method, GnomeVFSMethodHandle *method_handle, GnomeVFSFileInfo *file_info, GnomeVFSContext *context ) { DirHandle *handle = (DirHandle *) method_handle; mnode *file; mple_track *tptr; if ( !handle->current_child ) { return GNOME_VFS_ERROR_EOF; } file = handle->current_child->data; switch( file->type ) { case MNODE_ALBUM: case MNODE_DEVICE: case MNODE_ROOT: file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY; file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE; file_info->mime_type = g_strdup( "x-directory/normal" ); /* /device? */ file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE; break; case MNODE_TRACK: default: tptr = file->data; file_info->type = GNOME_VFS_FILE_TYPE_REGULAR; file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE; file_info->mime_type = g_strdup( "audio/mpeg" ); file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE; break; } file_info->name = g_strdup( file->displayname ); handle->current_child = handle->current_child->next; return GNOME_VFS_OK; } static GnomeVFSResult do_close_directory( GnomeVFSMethod *method, GnomeVFSMethodHandle *method_handle, GnomeVFSContext *context ) { DirHandle *handle = (DirHandle *)method_handle; g_free (handle); return GNOME_VFS_OK; } static GnomeVFSResult do_get_file_info( GnomeVFSMethod *method, GnomeVFSURI *uri, GnomeVFSFileInfo *file_info, GnomeVFSFileInfoOptions options, GnomeVFSContext *context ) { mnode *file; struct stat statbuf; mple_device *dev = NULL; mple_folder *fptr; mple_track *tptr; file = mnode_lookup( uri ); if ( file == NULL ) { return GNOME_VFS_ERROR_NOT_FOUND; } /* this is constant regardless of node type */ file_info->name = g_strdup( file->displayname ); file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE; if ( file->type == MNODE_ROOT ) { /* can't do much with this, really */ file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE | GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS | GNOME_VFS_FILE_INFO_FIELDS_ACCESS | GNOME_VFS_FILE_INFO_FIELDS_TYPE; file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY; file_info->mime_type = g_strdup( "x-directory/normal" ); file_info->permissions = GNOME_VFS_PERM_USER_READ | GNOME_VFS_PERM_USER_EXEC | GNOME_VFS_PERM_GROUP_READ | GNOME_VFS_PERM_GROUP_EXEC | GNOME_VFS_PERM_OTHER_READ | GNOME_VFS_PERM_OTHER_EXEC | GNOME_VFS_PERM_ACCESS_READABLE | GNOME_VFS_PERM_ACCESS_EXECUTABLE; return GNOME_VFS_OK; } if ( file->type == MNODE_TRACK ) { tptr = file->data; if ( mple_stat( tptr->dev, file->idx, &statbuf ) == -1 ) { return gnome_vfs_result_from_errno(); } file_info->mime_type = g_strdup( "audio/mpeg" ); file_info->type = GNOME_VFS_FILE_TYPE_REGULAR; file_info->size = statbuf.st_size; file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_SIZE; } else { gchar *s; if ( file->type == MNODE_ALBUM ) { fptr = file->data; dev = fptr->dev; } else { dev = file->data; } s = g_strdup_printf( dev->pbtemplate, dev->path, dev->master_pblist ); if ( stat( s, &statbuf ) == -1 ) { g_free( s ); return gnome_vfs_result_from_errno(); } g_free( s ); file_info->mime_type = g_strdup( "x-directory/normal" ); file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY; } GNOME_VFS_FILE_INFO_SET_LOCAL( file_info, TRUE ); file_info->permissions = statbuf.st_mode & ( S_IRWXU | S_IRWXG | S_IRWXO ); file_info->atime = statbuf.st_atime; file_info->mtime = statbuf.st_mtime; file_info->ctime = statbuf.st_ctime; file_info->uid = statbuf.st_uid; file_info->gid = statbuf.st_gid; file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE | GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS | GNOME_VFS_FILE_INFO_FIELDS_FLAGS | GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE | GNOME_VFS_FILE_INFO_FIELDS_ATIME | GNOME_VFS_FILE_INFO_FIELDS_MTIME | GNOME_VFS_FILE_INFO_FIELDS_CTIME; return GNOME_VFS_OK; } static gboolean do_is_local( GnomeVFSMethod *method, const GnomeVFSURI *uri ) { return TRUE; } static GnomeVFSResult do_make_directory( GnomeVFSMethod *method, GnomeVFSURI *uri, guint perm, GnomeVFSContext *context ) { GnomeVFSResult result = GNOME_VFS_ERROR_NOT_PERMITTED; mnode *node = NULL, *file = NULL; mple_folder *folder = NULL; guint16 *newname = NULL; const gchar *path = gnome_vfs_uri_get_path( uri ); gchar **bits = NULL, *upath = NULL; guint16 fnum = 0; GnomeVFSURI *device_uri = NULL; guint32 convlen = 0; /* convert the URI to a path */ if ( path != NULL ) { upath = gnome_vfs_unescape_string( path, NULL ); } else { /* can't make a directory at root! */ goto out; } /* now find the device that owns this path */ bits = g_strsplit( upath, "/", 0 ); if ( bits[0] == NULL || bits[1] == NULL || bits[2] == NULL || bits[3] != NULL ) { /* you've tried to create a folder somewhere that's not allowed */ goto out; } /* up one step = device path */ device_uri = gnome_vfs_uri_get_parent( uri ); node = mnode_lookup( device_uri ); if ( node == NULL ) { result = GNOME_VFS_ERROR_NOT_FOUND; goto out; } /* build a folder naming structure (mildly overkill) */ folder = g_malloc0( sizeof( mple_folder )); folder->dev = node->data; folder->tracklist = NULL; newname = utf8_to_utf16be( bits[2], 126, &convlen ); if ( newname == NULL ) { result = gnome_vfs_result_from_errno(); goto out; } memcpy( folder->folderdata.foldername, newname, convlen ); /* add the folder */ if (( fnum = mple_add_folder( folder->dev, &folder->folderdata, 0 )) == 0 ) { goto out; } /* now integrate the folder into the tree */ file = mnode_new( fnum, bits[2] ); if (file) { G_LOCK(musicroot); file->gnode = g_node_append_data( node->gnode, file ); file->type = MNODE_ALBUM; file->data = g_list_nth_data( folder->dev->folderlist, fnum - 1 ); G_UNLOCK(musicroot); result = GNOME_VFS_OK; } else { result = gnome_vfs_result_from_errno(); } out: if ( folder != NULL ) { g_free( folder ); } if ( newname != NULL ) { g_free( newname ); } if ( bits != NULL ) { g_strfreev( bits ); } if ( upath != NULL ) { g_free( upath ); } return result; } static GnomeVFSResult do_remove_directory( GnomeVFSMethod *method, GnomeVFSURI *uri, GnomeVFSContext *context ) { mnode *file = NULL; mple_folder *fptr = NULL; GNode *node; file = mnode_lookup( uri ); if ( file == NULL ) { return GNOME_VFS_ERROR_INVALID_URI; } /* Can't remove the root. */ if ( file->gnode == musicroot ) { return GNOME_VFS_ERROR_NOT_PERMITTED; } /* Can't remove non-empty directories. */ if ( g_node_n_children( file->gnode ) > 0 ) { return GNOME_VFS_ERROR_NOT_PERMITTED; } /* remove node from tree */ G_LOCK( musicroot ); fptr = file->data; if ( !mple_del_folder( fptr->dev, file->idx )) { G_UNLOCK( musicroot ); return gnome_vfs_result_from_errno(); } /* this will have been freed by mple_del_folder() */ file->data = NULL; node = file->gnode; g_node_unlink( node ); g_node_traverse( node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, mnode_free_func, NULL ); g_node_destroy( node ); G_UNLOCK( musicroot ); dump_tree( musicroot ); return GNOME_VFS_OK; } static GnomeVFSResult do_unlink( GnomeVFSMethod *method, GnomeVFSURI *uri, GnomeVFSContext *context ) { mnode *file = mnode_lookup( uri ); mple_track *tptr = NULL; GNode *gnode = NULL; if ( file == NULL ) { return GNOME_VFS_ERROR_INVALID_URI; } /* Can't remove the root. */ if ( file->gnode == musicroot ) { return GNOME_VFS_ERROR_NOT_PERMITTED; } /* Can't remove directories. */ if ( file->type != MNODE_TRACK ) { return GNOME_VFS_ERROR_NOT_PERMITTED; } G_LOCK( musicroot ); tptr = file->data; if ( !mple_del_track( tptr->dev, file->idx )) { G_UNLOCK( musicroot ); return gnome_vfs_result_from_errno(); } /* this will have been nuked by the call to mple_del_track */ file->data = NULL; gnode = file->gnode; g_node_unlink( gnode ); g_node_traverse( gnode, G_PRE_ORDER, G_TRAVERSE_ALL, -1, mnode_free_func, NULL ); g_node_destroy( gnode ); G_UNLOCK( musicroot ); return GNOME_VFS_OK; } static GnomeVFSResult do_check_same_fs( GnomeVFSMethod *method, GnomeVFSURI *a, GnomeVFSURI *b, gboolean *same_fs_return, GnomeVFSContext *context ) { const gchar *patha = gnome_vfs_uri_get_path( a ); const gchar *pathb = gnome_vfs_uri_get_path( b ); gchar **bitsa = NULL, **bitsb = NULL, *upatha = NULL, *upathb = NULL; gboolean retval = FALSE; GnomeVFSResult result = GNOME_VFS_OK; if ( patha != NULL ) { upatha = gnome_vfs_unescape_string( patha, NULL ); } else { goto out; } if ( pathb != NULL ) { upathb = gnome_vfs_unescape_string( pathb, NULL ); } else { goto out; } bitsa = g_strsplit( upatha, "/", 0 ); bitsb = g_strsplit( upathb, "/", 0 ); if ( strcmp( bitsa[1], bitsb[1] ) == 0 ) { retval = TRUE; } out: if ( bitsb != NULL ) { g_strfreev( bitsb ); } if ( bitsa != NULL ) { g_strfreev( bitsa ); } if ( upathb != NULL ) { g_free( upathb ); } if ( upatha != NULL ) { g_free( upatha ); } return result; } static GnomeVFSResult do_set_file_info( GnomeVFSMethod *method, GnomeVFSURI *uri, const GnomeVFSFileInfo *info, GnomeVFSSetFileInfoMask mask, GnomeVFSContext *context ) { GnomeVFSResult res = GNOME_VFS_ERROR_NOT_PERMITTED; mnode *file = NULL; guint16 *newname = NULL; mple_device *dev = NULL; mple_folder *fptr = NULL; mple_track *tptr = NULL; pblist_folder newfolder; pblist_track newtrack; guint32 convlen = 0; if (( file = mnode_lookup( uri )) == NULL ) { res = GNOME_VFS_ERROR_NOT_FOUND; goto out; } /* not entirely sure how to do this - should I refuse all changes if other bits are set, or just change what I can? */ if ( !(mask & GNOME_VFS_SET_FILE_INFO_NAME )) { goto out; } if ( strlen( info->name ) > 127 - ( file->type == MNODE_ALBUM ? 1 : 0 )) { res = GNOME_VFS_ERROR_BAD_PARAMETERS; goto out; } newname = utf8_to_utf16be( info->name, strlen( info->name ), &convlen ); if ( newname == NULL ) { res = gnome_vfs_result_from_errno(); goto out; } switch( file->type ) { case MNODE_ROOT: goto out; case MNODE_DEVICE: /* need to figure out how to allow device-label frobbing and suchlike */ goto out; case MNODE_ALBUM: memset( &(newfolder.foldername ), 0, 126 ); memcpy( &(newfolder.foldername), newname, convlen ); fptr = file->data; dev = fptr->dev; if ( mple_ren_folder( dev, file->idx, &newfolder ) == 0 ) { res = gnome_vfs_result_from_errno(); } else { g_free( file->displayname ); file->displayname = g_strdup( info->name ); res = GNOME_VFS_OK; } goto out; case MNODE_TRACK: memset( &(newtrack.filename), 0, 127 ); memcpy( &(newtrack.filename), newname, convlen ); tptr = file->data; dev = tptr->dev; if ( mple_ren_track( dev, tptr->tracknum, &newtrack ) == 0 ) { res = gnome_vfs_result_from_errno(); } else { g_free( file->displayname ); file->displayname = g_strdup( info->name ); res = GNOME_VFS_OK; } goto out; default: goto out; } out: if ( newname ) { g_free( newname ); } return res; } static GnomeVFSMethod method = { sizeof (GnomeVFSMethod), do_open, /* open */ do_create, /* create */ do_close, /* close */ do_read, /* read */ do_write, /* write */ do_seek, /* seek */ do_tell, /* tell */ NULL, /* truncate_handle */ do_open_directory, /* do_open_directory */ do_close_directory, /* do_close_directory */ do_read_directory, /* do_read_directory */ do_get_file_info, /* do_get_file_info */ NULL, /* get_file_info_from_handle */ do_is_local, /* is_local */ do_make_directory, /* make_directory */ do_remove_directory, /* remove_directory */ NULL, /* move */ do_unlink, /* unlink */ do_check_same_fs, /* check_same_fs */ do_set_file_info, /* set_file_info */ NULL, /* truncate */ NULL, /* find_directory */ NULL, /* create_symbolic_link */ NULL, /* monitor_add */ NULL, /* monitor_cancel */ NULL /* file_control */ }; GnomeVFSMethod * vfs_module_init( const char *method_name, const char *args ) { if (strcmp (method_name, "mple") == 0) { init_root(); return &method; } return NULL; } void vfs_module_shutdown( GnomeVFSMethod* method ) { free_root(); }