@ -11,17 +11,16 @@
# include "qemu/osdep.h"
# include "qemu/osdep.h"
# include "qapi/error.h"
# include "qapi/error.h"
# include "qemu/error-report.h"
# include <wchar.h>
# include <wchar.h>
# include <dirent.h>
# include <dirent.h>
# include <sys/statvfs.h>
# include <sys/statvfs.h>
# ifdef CONFIG_INOTIFY1
# include <sys/inotify.h>
# include "qemu/main-loop.h"
# endif
# include "qemu-common.h"
# include "qemu-common.h"
# include "qemu/iov.h"
# include "qemu/iov.h"
# include "qemu/filemonitor.h"
# include "trace.h"
# include "trace.h"
# include "hw/usb.h"
# include "hw/usb.h"
# include "desc.h"
# include "desc.h"
@ -132,7 +131,6 @@ enum {
EP_EVENT ,
EP_EVENT ,
} ;
} ;
# ifdef CONFIG_INOTIFY1
typedef struct MTPMonEntry MTPMonEntry ;
typedef struct MTPMonEntry MTPMonEntry ;
struct MTPMonEntry {
struct MTPMonEntry {
@ -141,7 +139,6 @@ struct MTPMonEntry {
QTAILQ_ENTRY ( MTPMonEntry ) next ;
QTAILQ_ENTRY ( MTPMonEntry ) next ;
} ;
} ;
# endif
struct MTPControl {
struct MTPControl {
uint16_t code ;
uint16_t code ;
@ -172,10 +169,8 @@ struct MTPObject {
char * name ;
char * name ;
char * path ;
char * path ;
struct stat stat ;
struct stat stat ;
# ifdef CONFIG_INOTIFY1
/* file monitor watch id */
/* inotify watch cookie */
int watchid ;
int watchfd ;
# endif
MTPObject * parent ;
MTPObject * parent ;
uint32_t nchildren ;
uint32_t nchildren ;
QLIST_HEAD ( , MTPObject ) children ;
QLIST_HEAD ( , MTPObject ) children ;
@ -198,11 +193,8 @@ struct MTPState {
bool readonly ;
bool readonly ;
QTAILQ_HEAD ( , MTPObject ) objects ;
QTAILQ_HEAD ( , MTPObject ) objects ;
# ifdef CONFIG_INOTIFY1
QFileMonitor * file_monitor ;
/* inotify descriptor */
int inotifyfd ;
QTAILQ_HEAD ( , MTPMonEntry ) events ;
QTAILQ_HEAD ( , MTPMonEntry ) events ;
# endif
/* Responder is expecting a write operation */
/* Responder is expecting a write operation */
bool write_pending ;
bool write_pending ;
struct {
struct {
@ -391,6 +383,7 @@ static MTPObject *usb_mtp_object_alloc(MTPState *s, uint32_t handle,
goto ignore ;
goto ignore ;
}
}
o - > watchid = - 1 ;
o - > handle = handle ;
o - > handle = handle ;
o - > parent = parent ;
o - > parent = parent ;
o - > name = g_strdup ( name ) ;
o - > name = g_strdup ( name ) ;
@ -437,6 +430,10 @@ static void usb_mtp_object_free(MTPState *s, MTPObject *o)
trace_usb_mtp_object_free ( s - > dev . addr , o - > handle , o - > path ) ;
trace_usb_mtp_object_free ( s - > dev . addr , o - > handle , o - > path ) ;
if ( o - > watchid ! = - 1 & & s - > file_monitor ) {
qemu_file_monitor_remove_watch ( s - > file_monitor , o - > path , o - > watchid ) ;
}
QTAILQ_REMOVE ( & s - > objects , o , next ) ;
QTAILQ_REMOVE ( & s - > objects , o , next ) ;
if ( o - > parent ) {
if ( o - > parent ) {
QLIST_REMOVE ( o , list ) ;
QLIST_REMOVE ( o , list ) ;
@ -488,6 +485,10 @@ static MTPObject *usb_mtp_object_lookup_name(MTPObject *parent,
{
{
MTPObject * iter ;
MTPObject * iter ;
if ( len = = - 1 ) {
len = strlen ( name ) ;
}
QLIST_FOREACH ( iter , & parent - > children , list ) {
QLIST_FOREACH ( iter , & parent - > children , list ) {
if ( strncmp ( iter - > name , name , len ) = = 0 ) {
if ( strncmp ( iter - > name , name , len ) = = 0 ) {
return iter ;
return iter ;
@ -497,13 +498,12 @@ static MTPObject *usb_mtp_object_lookup_name(MTPObject *parent,
return NULL ;
return NULL ;
}
}
# ifdef CONFIG_INOTIFY1
static MTPObject * usb_mtp_object_lookup_id ( MTPState * s , int id )
static MTPObject * usb_mtp_object_lookup_wd ( MTPState * s , int wd )
{
{
MTPObject * iter ;
MTPObject * iter ;
QTAILQ_FOREACH ( iter , & s - > objects , next ) {
QTAILQ_FOREACH ( iter , & s - > objects , next ) {
if ( iter - > watchfd = = w d ) {
if ( iter - > watchid = = i d ) {
return iter ;
return iter ;
}
}
}
}
@ -511,159 +511,103 @@ static MTPObject *usb_mtp_object_lookup_wd(MTPState *s, int wd)
return NULL ;
return NULL ;
}
}
static void inotify_watchfn ( void * arg )
static void file_monitor_event ( int id ,
QFileMonitorEvent ev ,
const char * name ,
void * opaque )
{
{
MTPState * s = arg ;
MTPState * s = opaque ;
ssize_t bytes ;
MTPObject * parent = usb_mtp_object_lookup_id ( s , id ) ;
/* From the man page: atleast one event can be read */
MTPMonEntry * entry = NULL ;
int pos ;
MTPObject * o ;
char buf [ sizeof ( struct inotify_event ) + NAME_MAX + 1 ] ;
if ( ! parent ) {
for ( ; ; ) {
return ;
bytes = read ( s - > inotifyfd , buf , sizeof ( buf ) ) ;
}
pos = 0 ;
switch ( ev ) {
if ( bytes < = 0 ) {
case QFILE_MONITOR_EVENT_CREATED :
/* Better luck next time */
if ( usb_mtp_object_lookup_name ( parent , name , - 1 ) ) {
/* Duplicate create event */
return ;
return ;
}
}
entry = g_new0 ( MTPMonEntry , 1 ) ;
entry - > handle = s - > next_handle ;
entry - > event = EVT_OBJ_ADDED ;
o = usb_mtp_add_child ( s , parent , name ) ;
if ( ! o ) {
g_free ( entry ) ;
return ;
}
trace_usb_mtp_file_monitor_event ( s - > dev . addr , name , " Obj Added " ) ;
break ;
case QFILE_MONITOR_EVENT_DELETED :
/*
/*
* TODO : Ignore initiator initiated events .
* The kernel issues a IN_IGNORED event
* For now we are good because the store is RO
* when a dir containing a watchpoint is
* deleted , so we don ' t have to delete the
* watchpoint
*/
*/
while ( bytes > 0 ) {
o = usb_mtp_object_lookup_name ( parent , name , - 1 ) ;
char * p = buf + pos ;
if ( ! o ) {
struct inotify_event * event = ( struct inotify_event * ) p ;
return ;
int watchfd = 0 ;
uint32_t mask = event - > mask & ( IN_CREATE | IN_DELETE |
IN_MODIFY | IN_IGNORED ) ;
MTPObject * parent = usb_mtp_object_lookup_wd ( s , event - > wd ) ;
MTPMonEntry * entry = NULL ;
MTPObject * o ;
pos = pos + sizeof ( struct inotify_event ) + event - > len ;
bytes = bytes - pos ;
if ( ! parent ) {
continue ;
}
switch ( mask ) {
case IN_CREATE :
if ( usb_mtp_object_lookup_name
( parent , event - > name , event - > len ) ) {
/* Duplicate create event */
continue ;
}
entry = g_new0 ( MTPMonEntry , 1 ) ;
entry - > handle = s - > next_handle ;
entry - > event = EVT_OBJ_ADDED ;
o = usb_mtp_add_child ( s , parent , event - > name ) ;
if ( ! o ) {
g_free ( entry ) ;
continue ;
}
o - > watchfd = watchfd ;
trace_usb_mtp_inotify_event ( s - > dev . addr , event - > name ,
event - > mask , " Obj Added " ) ;
break ;
case IN_DELETE :
/*
* The kernel issues a IN_IGNORED event
* when a dir containing a watchpoint is
* deleted , so we don ' t have to delete the
* watchpoint
*/
o = usb_mtp_object_lookup_name ( parent , event - > name , event - > len ) ;
if ( ! o ) {
continue ;
}
entry = g_new0 ( MTPMonEntry , 1 ) ;
entry - > handle = o - > handle ;
entry - > event = EVT_OBJ_REMOVED ;
trace_usb_mtp_inotify_event ( s - > dev . addr , o - > path ,
event - > mask , " Obj Deleted " ) ;
usb_mtp_object_free ( s , o ) ;
break ;
case IN_MODIFY :
o = usb_mtp_object_lookup_name ( parent , event - > name , event - > len ) ;
if ( ! o ) {
continue ;
}
entry = g_new0 ( MTPMonEntry , 1 ) ;
entry - > handle = o - > handle ;
entry - > event = EVT_OBJ_INFO_CHANGED ;
trace_usb_mtp_inotify_event ( s - > dev . addr , o - > path ,
event - > mask , " Obj Modified " ) ;
break ;
case IN_IGNORED :
trace_usb_mtp_inotify_event ( s - > dev . addr , parent - > path ,
event - > mask , " Obj parent dir ignored " ) ;
break ;
default :
fprintf ( stderr , " usb-mtp: failed to parse inotify event \n " ) ;
continue ;
}
if ( entry ) {
QTAILQ_INSERT_HEAD ( & s - > events , entry , next ) ;
}
}
}
}
entry = g_new0 ( MTPMonEntry , 1 ) ;
}
entry - > handle = o - > handle ;
entry - > event = EVT_OBJ_REMOVED ;
trace_usb_mtp_file_monitor_event ( s - > dev . addr , o - > path , " Obj Deleted " ) ;
usb_mtp_object_free ( s , o ) ;
break ;
static int usb_mtp_inotify_init ( MTPState * s )
case QFILE_MONITOR_EVENT_MODIFIED :
{
o = usb_mtp_object_lookup_name ( parent , name , - 1 ) ;
int fd ;
if ( ! o ) {
return ;
}
entry = g_new0 ( MTPMonEntry , 1 ) ;
entry - > handle = o - > handle ;
entry - > event = EVT_OBJ_INFO_CHANGED ;
trace_usb_mtp_file_monitor_event ( s - > dev . addr , o - > path , " Obj Modified " ) ;
break ;
fd = inotify_init1 ( IN_NONBLOCK ) ;
case QFILE_MONITOR_EVENT_IGNORED :
if ( fd = = - 1 ) {
trace_usb_mtp_file_monitor_event ( s - > dev . addr , parent - > path ,
return 1 ;
" Obj parent dir ignored " ) ;
}
break ;
QTAILQ_INIT ( & s - > events ) ;
case QFILE_MONITOR_EVENT_ATTRIBUTES :
s - > inotifyfd = fd ;
break ;
qemu_set_fd_handler ( fd , inotify_watchfn , NULL , s ) ;
default :
g_assert_not_reached ( ) ;
}
return 0 ;
if ( entry ) {
QTAILQ_INSERT_HEAD ( & s - > events , entry , next ) ;
}
}
}
static void usb_mtp_inotify _cleanup ( MTPState * s )
static void usb_mtp_file_monitor_cleanup ( MTPState * s )
{
{
MTPMonEntry * e , * p ;
MTPMonEntry * e , * p ;
if ( ! s - > inotifyfd ) {
return ;
}
qemu_set_fd_handler ( s - > inotifyfd , NULL , NULL , s ) ;
close ( s - > inotifyfd ) ;
QTAILQ_FOREACH_SAFE ( e , & s - > events , next , p ) {
QTAILQ_FOREACH_SAFE ( e , & s - > events , next , p ) {
QTAILQ_REMOVE ( & s - > events , e , next ) ;
QTAILQ_REMOVE ( & s - > events , e , next ) ;
g_free ( e ) ;
g_free ( e ) ;
}
}
}
static int usb_mtp_add_watch ( int inotifyfd , const char * path )
qemu_file_monitor_free ( s - > file_monitor ) ;
{
s - > file_monitor = NULL ;
uint32_t mask = IN_CREATE | IN_DELETE | IN_MODIFY ;
return inotify_add_watch ( inotifyfd , path , mask ) ;
}
}
# endif
static void usb_mtp_object_readdir ( MTPState * s , MTPObject * o )
static void usb_mtp_object_readdir ( MTPState * s , MTPObject * o )
{
{
struct dirent * entry ;
struct dirent * entry ;
DIR * dir ;
DIR * dir ;
int fd ;
int fd ;
Error * err = NULL ;
if ( o - > have_children ) {
if ( o - > have_children ) {
return ;
return ;
@ -679,16 +623,21 @@ static void usb_mtp_object_readdir(MTPState *s, MTPObject *o)
close ( fd ) ;
close ( fd ) ;
return ;
return ;
}
}
# ifdef CONFIG_INOTIFY1
int watchfd = usb_mtp_add_watch ( s - > inotifyfd , o - > path ) ;
if ( s - > file_monitor ) {
if ( watchfd = = - 1 ) {
int id = qemu_file_monitor_add_watch ( s - > file_monitor , o - > path , NULL ,
fprintf ( stderr , " usb-mtp: failed to add watch for %s \n " , o - > path ) ;
file_monitor_event , s , & err ) ;
} else {
if ( id = = - 1 ) {
trace_usb_mtp_inotify_event ( s - > dev . addr , o - > path ,
error_report ( " usb-mtp: failed to add watch for %s: %s " , o - > path ,
0 , " Watch Added " ) ;
error_get_pretty ( err ) ) ;
o - > watchfd = watchfd ;
error_free ( err ) ;
} else {
trace_usb_mtp_file_monitor_event ( s - > dev . addr , o - > path ,
" Watch Added " ) ;
o - > watchid = id ;
}
}
}
# endif
while ( ( entry = readdir ( dir ) ) ! = NULL ) {
while ( ( entry = readdir ( dir ) ) ! = NULL ) {
usb_mtp_add_child ( s , o , entry - > d_name ) ;
usb_mtp_add_child ( s , o , entry - > d_name ) ;
}
}
@ -1196,13 +1145,11 @@ enum {
/* Assumes that children, if any, have been already freed */
/* Assumes that children, if any, have been already freed */
static void usb_mtp_object_free_one ( MTPState * s , MTPObject * o )
static void usb_mtp_object_free_one ( MTPState * s , MTPObject * o )
{
{
# ifndef CONFIG_INOTIFY1
assert ( o - > nchildren = = 0 ) ;
assert ( o - > nchildren = = 0 ) ;
QTAILQ_REMOVE ( & s - > objects , o , next ) ;
QTAILQ_REMOVE ( & s - > objects , o , next ) ;
g_free ( o - > name ) ;
g_free ( o - > name ) ;
g_free ( o - > path ) ;
g_free ( o - > path ) ;
g_free ( o ) ;
g_free ( o ) ;
# endif
}
}
static int usb_mtp_deletefn ( MTPState * s , MTPObject * o , uint32_t trans )
static int usb_mtp_deletefn ( MTPState * s , MTPObject * o , uint32_t trans )
@ -1301,6 +1248,7 @@ static void usb_mtp_command(MTPState *s, MTPControl *c)
MTPData * data_in = NULL ;
MTPData * data_in = NULL ;
MTPObject * o = NULL ;
MTPObject * o = NULL ;
uint32_t nres = 0 , res0 = 0 ;
uint32_t nres = 0 , res0 = 0 ;
Error * err = NULL ;
/* sanity checks */
/* sanity checks */
if ( c - > code > = CMD_CLOSE_SESSION & & s - > session = = 0 ) {
if ( c - > code > = CMD_CLOSE_SESSION & & s - > session = = 0 ) {
@ -1328,19 +1276,21 @@ static void usb_mtp_command(MTPState *s, MTPControl *c)
trace_usb_mtp_op_open_session ( s - > dev . addr ) ;
trace_usb_mtp_op_open_session ( s - > dev . addr ) ;
s - > session = c - > argv [ 0 ] ;
s - > session = c - > argv [ 0 ] ;
usb_mtp_object_alloc ( s , s - > next_handle + + , NULL , s - > root ) ;
usb_mtp_object_alloc ( s , s - > next_handle + + , NULL , s - > root ) ;
# ifdef CONFIG_INOTIFY1
if ( usb_mtp_inotify_init ( s ) ) {
s - > file_monitor = qemu_file_monitor_new ( & err ) ;
fprintf ( stderr , " usb-mtp: file monitoring init failed \n " ) ;
if ( err ) {
error_report ( " usb-mtp: file monitoring init failed: %s " ,
error_get_pretty ( err ) ) ;
error_free ( err ) ;
} else {
QTAILQ_INIT ( & s - > events ) ;
}
}
# endif
break ;
break ;
case CMD_CLOSE_SESSION :
case CMD_CLOSE_SESSION :
trace_usb_mtp_op_close_session ( s - > dev . addr ) ;
trace_usb_mtp_op_close_session ( s - > dev . addr ) ;
s - > session = 0 ;
s - > session = 0 ;
s - > next_handle = 0 ;
s - > next_handle = 0 ;
# ifdef CONFIG_INOTIFY1
usb_mtp_file_monitor_cleanup ( s ) ;
usb_mtp_inotify_cleanup ( s ) ;
# endif
usb_mtp_object_free ( s , QTAILQ_FIRST ( & s - > objects ) ) ;
usb_mtp_object_free ( s , QTAILQ_FIRST ( & s - > objects ) ) ;
assert ( QTAILQ_EMPTY ( & s - > objects ) ) ;
assert ( QTAILQ_EMPTY ( & s - > objects ) ) ;
break ;
break ;
@ -1553,9 +1503,7 @@ static void usb_mtp_handle_reset(USBDevice *dev)
trace_usb_mtp_reset ( s - > dev . addr ) ;
trace_usb_mtp_reset ( s - > dev . addr ) ;
# ifdef CONFIG_INOTIFY1
usb_mtp_file_monitor_cleanup ( s ) ;
usb_mtp_inotify_cleanup ( s ) ;
# endif
usb_mtp_object_free ( s , QTAILQ_FIRST ( & s - > objects ) ) ;
usb_mtp_object_free ( s , QTAILQ_FIRST ( & s - > objects ) ) ;
s - > session = 0 ;
s - > session = 0 ;
usb_mtp_data_free ( s - > data_in ) ;
usb_mtp_data_free ( s - > data_in ) ;
@ -2026,7 +1974,6 @@ static void usb_mtp_handle_data(USBDevice *dev, USBPacket *p)
}
}
break ;
break ;
case EP_EVENT :
case EP_EVENT :
# ifdef CONFIG_INOTIFY1
if ( ! QTAILQ_EMPTY ( & s - > events ) ) {
if ( ! QTAILQ_EMPTY ( & s - > events ) ) {
struct MTPMonEntry * e = QTAILQ_LAST ( & s - > events ) ;
struct MTPMonEntry * e = QTAILQ_LAST ( & s - > events ) ;
uint32_t handle ;
uint32_t handle ;
@ -2050,7 +1997,6 @@ static void usb_mtp_handle_data(USBDevice *dev, USBPacket *p)
g_free ( e ) ;
g_free ( e ) ;
return ;
return ;
}
}
# endif
p - > status = USB_RET_NAK ;
p - > status = USB_RET_NAK ;
return ;
return ;
default :
default :