From: Dick Porter Date: Fri, 21 May 2004 19:46:43 +0000 (-0000) Subject: 2004-05-21 Dick Porter X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=commitdiff_plain;h=dc53da821c43f6b6a51f4e7ba0ccb4d92e93379c;p=mono.git 2004-05-21 Dick Porter * io.c (CreateFile): Check for existing share modes when opening a file. * handles.c: * handles-private.h: * daemon-messages.h: * daemon.c: Maintain a hash of file share modes, keying on device and inode (to cope with symlinks.) svn path=/trunk/mono/; revision=27850 --- diff --git a/mono/io-layer/ChangeLog b/mono/io-layer/ChangeLog index 2b87f5f4a93..9e069d7ba7a 100644 --- a/mono/io-layer/ChangeLog +++ b/mono/io-layer/ChangeLog @@ -1,3 +1,14 @@ +2004-05-21 Dick Porter + + * io.c (CreateFile): Check for existing share modes when opening + a file. + + * handles.c: + * handles-private.h: + * daemon-messages.h: + * daemon.c: Maintain a hash of file share modes, keying on device + and inode (to cope with symlinks.) + 2004-05-20 Lluis Sanchez Gual * daemon-messages.c: Retry if the communication with the daemon is diff --git a/mono/io-layer/daemon-messages.h b/mono/io-layer/daemon-messages.h index 1b21f08c57d..237e951b5e6 100644 --- a/mono/io-layer/daemon-messages.h +++ b/mono/io-layer/daemon-messages.h @@ -20,7 +20,9 @@ typedef enum { WapiHandleRequestType_Scratch, WapiHandleRequestType_ScratchFree, WapiHandleRequestType_ProcessFork, - WapiHandleRequestType_ProcessKill + WapiHandleRequestType_ProcessKill, + WapiHandleRequestType_GetOrSetShare, + WapiHandleRequestType_SetShare } WapiHandleRequestType; typedef struct @@ -65,6 +67,22 @@ typedef struct { gint32 signo; } WapiHandleRequest_ProcessKill; +typedef struct +{ + dev_t device; + ino_t inode; + guint32 new_sharemode; + guint32 new_access; +} WapiHandleRequest_GetOrSetShare; + +typedef struct +{ + dev_t device; + ino_t inode; + guint32 sharemode; + guint32 access; +} WapiHandleRequest_SetShare; + typedef struct { WapiHandleRequestType type; @@ -77,6 +95,8 @@ typedef struct WapiHandleRequest_ScratchFree scratch_free; WapiHandleRequest_ProcessFork process_fork; WapiHandleRequest_ProcessKill process_kill; + WapiHandleRequest_GetOrSetShare get_or_set_share; + WapiHandleRequest_SetShare set_share; } u; } WapiHandleRequest; @@ -88,7 +108,9 @@ typedef enum { WapiHandleResponseType_Scratch, WapiHandleResponseType_ScratchFree, WapiHandleResponseType_ProcessFork, - WapiHandleResponseType_ProcessKill + WapiHandleResponseType_ProcessKill, + WapiHandleResponseType_GetOrSetShare, + WapiHandleResponseType_SetShare } WapiHandleResponseType; typedef struct @@ -137,6 +159,18 @@ typedef struct guint32 err; } WapiHandleResponse_ProcessKill; +typedef struct +{ + gboolean exists; + guint32 sharemode; + guint32 access; +} WapiHandleResponse_GetOrSetShare; + +typedef struct +{ + guint32 dummy; +} WapiHandleResponse_SetShare; + typedef struct { WapiHandleResponseType type; @@ -150,6 +184,8 @@ typedef struct WapiHandleResponse_ScratchFree scratch_free; WapiHandleResponse_ProcessFork process_fork; WapiHandleResponse_ProcessKill process_kill; + WapiHandleResponse_GetOrSetShare get_or_set_share; + WapiHandleResponse_SetShare set_share; } u; } WapiHandleResponse; diff --git a/mono/io-layer/daemon.c b/mono/io-layer/daemon.c index 01fb5da616d..746fba49d17 100644 --- a/mono/io-layer/daemon.c +++ b/mono/io-layer/daemon.c @@ -62,9 +62,24 @@ static int main_sock; /* Set to TRUE by the SIGCHLD signal handler */ static volatile gboolean check_processes=FALSE; +/* The file_share_hash is used to emulate the windows file sharing mode */ +typedef struct _share_key +{ + dev_t device; + ino_t inode; +} ShareKey; + +typedef struct _share_data +{ + guint32 sharemode; + guint32 access; +} ShareData; + +static GHashTable *file_share_hash = NULL; + static gboolean fd_activity (GIOChannel *channel, GIOCondition condition, gpointer data); - +static void check_sharing (dev_t device, ino_t inode); /* Deletes the shared memory segment. If we're exiting on error, * clients will get EPIPEs. @@ -194,10 +209,27 @@ static void sigchld_handler (int unused) check_processes=TRUE; } +static guint sharedata_hash (gconstpointer key) +{ + ShareKey *sharekey = (ShareKey *)key; + + return(g_int_hash (&(sharekey->inode))); +} + +static gboolean sharedata_equal (gconstpointer a, gconstpointer b) +{ + ShareKey *share_a = (ShareKey *)a; + ShareKey *share_b = (ShareKey *)b; + + return(share_a->device == share_b->device && + share_a->inode == share_b->inode); +} + /* * startup: * - * Bind signals and attach to shared memory + * Bind signals, attach to shared memory and set up any internal data + * structures needed. */ static void startup (void) { @@ -235,6 +267,10 @@ static void startup (void) "mono-handle-daemon-%d-%d-%ld", getuid (), getpid (), time (NULL)); #endif + + file_share_hash = g_hash_table_new_full (sharedata_hash, + sharedata_equal, g_free, + g_free); } @@ -310,6 +346,10 @@ static gboolean unref_handle (ChannelData *channel_data, guint32 handle) } if(_wapi_shared_data[segment]->handles[idx].ref==0) { + gboolean was_file; + dev_t device = 0; + ino_t inode = 0; + if (channel_data->open_handles[handle]!=0) { g_warning (G_GNUC_PRETTY_FUNCTION ": per-process open_handles mismatch, set to %d, should be 0", channel_data->open_handles[handle]); @@ -319,6 +359,30 @@ static gboolean unref_handle (ChannelData *channel_data, guint32 handle) g_message (G_GNUC_PRETTY_FUNCTION ": Destroying handle 0x%x", handle); #endif + + /* if this was a file handle, save the device and + * inode numbers so we can scan the share info data + * later to see if the last handle to a file has been + * closed, and delete the data if so. + */ + was_file = (_wapi_shared_data[segment]->handles[idx].type == WAPI_HANDLE_FILE); + if (was_file) { + struct _WapiHandle_file *file_handle; + gboolean ok; + + ok = _wapi_lookup_handle (GUINT_TO_POINTER (handle), + WAPI_HANDLE_FILE, + (gpointer *)&file_handle, + NULL); + if (ok == FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up file handle %x", + handle); + } else { + device = file_handle->device; + inode = file_handle->inode; + } + } _wapi_handle_ops_close_shared (GUINT_TO_POINTER (handle)); @@ -329,6 +393,10 @@ static gboolean unref_handle (ChannelData *channel_data, guint32 handle) memset (&_wapi_shared_data[segment]->handles[idx].u, '\0', sizeof(_wapi_shared_data[segment]->handles[idx].u)); _wapi_shared_data[segment]->handles[idx].type=WAPI_HANDLE_UNUSED; + + if (was_file) { + check_sharing (device, inode); + } } if(channel_data == daemon_channel_data) { @@ -466,6 +534,104 @@ static void rem_fd(GIOChannel *channel, ChannelData *channel_data) } } +static void sharemode_set (dev_t device, ino_t inode, guint32 sharemode, + guint32 access) +{ + ShareKey *sharekey; + ShareData *sharedata; + + sharekey = g_new (ShareKey, 1); + sharekey->device = device; + sharekey->inode = inode; + + sharedata = g_new (ShareData, 1); + sharedata->sharemode = sharemode; + sharedata->access = access; + + /* Setting share mode to include all access bits is really + * removing the share info + */ + if (sharemode == (FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE)) { + g_hash_table_remove (file_share_hash, sharekey); + } else { + g_hash_table_insert (file_share_hash, sharekey, sharedata); + } +} + +static gboolean sharemode_get (dev_t device, ino_t inode, guint32 *sharemode, + guint32 *access) +{ + ShareKey sharekey; + ShareData *sharedata; + + sharekey.device = device; + sharekey.inode = inode; + + sharedata = (ShareData *)g_hash_table_lookup (file_share_hash, + &sharekey); + if (sharedata == NULL) { + return(FALSE); + } + + *sharemode = sharedata->sharemode; + *access = sharedata->access; + + return(TRUE); +} + +static gboolean share_compare (gpointer handle, gpointer user_data) +{ + struct _WapiHandle_file *file_handle; + gboolean ok; + ShareKey *sharekey = (ShareKey *)user_data; + + ok = _wapi_lookup_handle (handle, WAPI_HANDLE_FILE, + (gpointer *)&file_handle, NULL); + if (ok == FALSE) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": error looking up file handle %p", handle); + return(FALSE); + } + + if (file_handle->device == sharekey->device && + file_handle->inode == sharekey->inode) { +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": found one, handle %p", + handle); +#endif + return(TRUE); + } else { + return(FALSE); + } +} + +static void check_sharing (dev_t device, ino_t inode) +{ + ShareKey sharekey; + gpointer file_handle; + +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": Checking if anything has (dev 0x%llx, inode %lld) still open", device, inode); +#endif + + sharekey.device = device; + sharekey.inode = inode; + + file_handle = _wapi_search_handle (WAPI_HANDLE_FILE, share_compare, + &sharekey, NULL, NULL); + + if (file_handle == NULL) { + /* Delete this share info, as the last handle to it + * has been closed + */ +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": Deleting share data for (dev 0x%llx inode %lld)", device, inode); +#endif + + g_hash_table_remove (file_share_hash, &sharekey); + } +} + static gboolean process_compare (gpointer handle, gpointer user_data) { struct _WapiHandle_process *process_handle; @@ -1089,6 +1255,73 @@ static void process_process_fork (GIOChannel *channel, ChannelData *channel_data send_reply (channel, &resp); } +/* + * process_set_share: + * @channel: The client making the request + * @channel_data: The channel data + * @set_share: Set share data passed from the client + * + * Sets file share info + */ +static void process_set_share (GIOChannel *channel, ChannelData *channel_data, + WapiHandleRequest_SetShare set_share) +{ + WapiHandleResponse resp = {0}; + + resp.type = WapiHandleResponseType_SetShare; + +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": Setting share for file (dev:0x%llx, ino:%lld) mode 0x%x access 0x%x", set_share.device, set_share.inode, set_share.sharemode, set_share.access); +#endif + + sharemode_set (set_share.device, set_share.inode, set_share.sharemode, + set_share.access); + + send_reply (channel, &resp); +} + +/* + * process_get_or_set_share: + * @channel: The client making the request + * @channel_data: The channel data + * @get_share: GetOrSetShare data passed from the client + * + * Gets a file share status, and sets the status if it doesn't already + * exist + */ +static void process_get_or_set_share (GIOChannel *channel, + ChannelData *channel_data, + WapiHandleRequest_GetOrSetShare get_share) +{ + WapiHandleResponse resp = {0}; + + resp.type = WapiHandleResponseType_GetOrSetShare; + +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION + ": Getting share status for file (dev:0x%llx, ino:%lld)", + get_share.device, get_share.inode); +#endif + + resp.u.get_or_set_share.exists = sharemode_get (get_share.device, get_share.inode, &resp.u.get_or_set_share.sharemode, &resp.u.get_or_set_share.access); + + if (resp.u.get_or_set_share.exists) { +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": Share mode: 0x%x", + resp.u.get_or_set_share.sharemode); +#endif + } else { +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION + ": file share info not already known, setting"); +#endif + sharemode_set (get_share.device, get_share.inode, + get_share.new_sharemode, get_share.new_access); + } + + send_reply (channel, &resp); +} + /* * read_message: * @channel: The client to read the request from @@ -1150,6 +1383,13 @@ static gboolean read_message (GIOChannel *channel, ChannelData *channel_data) case WapiHandleRequestType_ProcessKill: process_process_kill (channel, req.u.process_kill); break; + case WapiHandleRequestType_SetShare: + process_set_share (channel, channel_data, req.u.set_share); + break; + case WapiHandleRequestType_GetOrSetShare: + process_get_or_set_share (channel, channel_data, + req.u.get_or_set_share); + break; case WapiHandleRequestType_Error: /* fall through */ default: diff --git a/mono/io-layer/handles-private.h b/mono/io-layer/handles-private.h index 4a6870335ef..674e01384b9 100644 --- a/mono/io-layer/handles-private.h +++ b/mono/io-layer/handles-private.h @@ -85,6 +85,13 @@ extern gboolean _wapi_handle_process_fork (guint32 cmd, guint32 env, extern gboolean _wapi_handle_process_kill (pid_t pid, guint32 signo, gint *err); +extern gboolean _wapi_handle_get_or_set_share (dev_t device, ino_t inode, + guint32 new_sharemode, + guint32 new_access, + guint32 *old_sharemode, + guint32 *old_access); +extern void _wapi_handle_set_share (dev_t device, ino_t inode, + guint32 sharemode, guint32 access); static inline struct _WapiHandleShared_list *_wapi_handle_get_shared_segment (guint32 segment) { diff --git a/mono/io-layer/handles.c b/mono/io-layer/handles.c index c61f5414451..769235fe76d 100644 --- a/mono/io-layer/handles.c +++ b/mono/io-layer/handles.c @@ -1656,3 +1656,66 @@ _wapi_handle_process_kill (pid_t process, guint32 signo, gint *errnum) return (result == 0); } +gboolean _wapi_handle_get_or_set_share (dev_t device, ino_t inode, + guint32 new_sharemode, + guint32 new_access, + guint32 *old_sharemode, + guint32 *old_access) +{ + WapiHandleRequest req = {0}; + WapiHandleResponse resp = {0}; + + if(shared != TRUE) { + /* No daemon means we don't know if a file is sharable. + * We're running in our own little world if this is + * the case, so there's no point in pretending that + * the file isn't sharable. + */ + return(FALSE); + } + + req.type = WapiHandleRequestType_GetOrSetShare; + req.u.get_or_set_share.device = device; + req.u.get_or_set_share.inode = inode; + req.u.get_or_set_share.new_sharemode = new_sharemode; + req.u.get_or_set_share.new_access = new_access; + + _wapi_daemon_request_response (daemon_sock, &req, &resp); + if (resp.type != WapiHandleResponseType_GetOrSetShare) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": bogus daemon response, type %d", resp.type); + g_assert_not_reached (); + } + + *old_sharemode = resp.u.get_or_set_share.sharemode; + *old_access = resp.u.get_or_set_share.access; + + return(resp.u.get_or_set_share.exists); +} + +void _wapi_handle_set_share (dev_t device, ino_t inode, guint32 sharemode, + guint32 access) +{ + WapiHandleRequest req = {0}; + WapiHandleResponse resp = {0}; + + if(shared != TRUE) { + /* No daemon, so there's no one else to tell about + * file sharing. + */ + return; + } + + req.type = WapiHandleRequestType_SetShare; + req.u.set_share.device = device; + req.u.set_share.inode = inode; + req.u.set_share.sharemode = sharemode; + req.u.set_share.access = access; + + _wapi_daemon_request_response (daemon_sock, &req, &resp); + if (resp.type != WapiHandleResponseType_SetShare) { + g_warning (G_GNUC_PRETTY_FUNCTION + ": bogus daemon response, type %d", resp.type); + g_assert_not_reached (); + } +} diff --git a/mono/io-layer/io-private.h b/mono/io-layer/io-private.h index b4dbbd39901..374be059956 100644 --- a/mono/io-layer/io-private.h +++ b/mono/io-layer/io-private.h @@ -30,6 +30,8 @@ struct _WapiHandle_file guint32 fileaccess; guint32 sharemode; guint32 attrs; + dev_t device; + ino_t inode; }; /* The boolean is for distinguishing between a zeroed struct being not diff --git a/mono/io-layer/io.c b/mono/io-layer/io.c index f4f93f1fd09..982a60938e6 100644 --- a/mono/io-layer/io.c +++ b/mono/io-layer/io.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #undef DEBUG @@ -1494,12 +1495,15 @@ gpointer CreateFile(const gunichar2 *name, guint32 fileaccess, int flags=convert_flags(fileaccess, createmode); mode_t perms=convert_perms(sharemode); gchar *filename; - int ret; + int fd, ret; int thr_ret; gpointer cf_ret = INVALID_HANDLE_VALUE; + struct stat statbuf; + gboolean file_already_shared; + guint32 file_existing_share, file_existing_access; mono_once (&io_ops_once, io_ops_init); - + if(name==NULL) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": name is NULL"); @@ -1518,7 +1522,11 @@ gpointer CreateFile(const gunichar2 *name, guint32 fileaccess, return(INVALID_HANDLE_VALUE); } - ret=open(filename, flags, perms); +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": Opening %s with share 0x%x and access 0x%x", filename, sharemode, fileaccess); +#endif + + fd = open(filename, flags, perms); /* If we were trying to open a directory with write permissions * (e.g. O_WRONLY or O_RDWR), this call will fail with @@ -1528,13 +1536,13 @@ gpointer CreateFile(const gunichar2 *name, guint32 fileaccess, * (e.g. utime()). Hence, if we failed with the EISDIR error, try * to open the directory again without write permission. */ - if (ret == -1 && errno == EISDIR) + if (fd == -1 && errno == EISDIR) { /* Try again but don't try to make it writable */ - ret=open(filename, flags & ~(O_RDWR|O_WRONLY), perms); + fd = open(filename, flags & ~(O_RDWR|O_WRONLY), perms); } - if(ret==-1) { + if (fd == -1) { #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION ": Error opening file %s: %s", filename, strerror(errno)); @@ -1545,12 +1553,75 @@ gpointer CreateFile(const gunichar2 *name, guint32 fileaccess, return(INVALID_HANDLE_VALUE); } + ret = fstat (fd, &statbuf); + if (ret == -1) { +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": fstat error of file %s: %s", filename, strerror (errno)); +#endif + _wapi_set_last_error_from_errno (); + g_free (filename); + close (fd); + + return(INVALID_HANDLE_VALUE); + } + + file_already_shared = _wapi_handle_get_or_set_share (statbuf.st_dev, statbuf.st_ino, sharemode, fileaccess, &file_existing_share, &file_existing_access); + + if (file_already_shared) { + if (file_existing_share == 0) { + /* Quick and easy, no possibility to share */ +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": Share mode prevents open: requested access: 0x%x, file has sharing = NONE", fileaccess); +#endif + SetLastError (ERROR_SHARING_VIOLATION); + g_free (filename); + close (fd); + + return(INVALID_HANDLE_VALUE); + } + + if (((file_existing_share == FILE_SHARE_READ) && + (fileaccess != GENERIC_READ)) || + ((file_existing_share == FILE_SHARE_WRITE) && + (fileaccess != GENERIC_WRITE))) { + /* New access mode doesn't match up */ +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": Share mode prevents open: requested access: 0x%x, file has sharing: 0x%x", fileaccess, file_existing_share); +#endif + SetLastError (ERROR_SHARING_VIOLATION); + g_free (filename); + close (fd); + + return(INVALID_HANDLE_VALUE); + } + + if (((file_existing_access & GENERIC_READ) && + !(sharemode & FILE_SHARE_READ)) || + ((file_existing_access & GENERIC_WRITE) && + !(sharemode & FILE_SHARE_WRITE))) { + /* New share mode doesn't match up */ +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": Access mode prevents open: requested share: 0x%x, file has access: 0x%x", sharemode, file_existing_access); +#endif + SetLastError (ERROR_SHARING_VIOLATION); + g_free (filename); + close (fd); + + return(INVALID_HANDLE_VALUE); + } + } else { +#ifdef DEBUG + g_message (G_GNUC_PRETTY_FUNCTION ": New file!"); +#endif + } + handle=_wapi_handle_new (WAPI_HANDLE_FILE); if(handle==_WAPI_HANDLE_INVALID) { g_warning (G_GNUC_PRETTY_FUNCTION ": error creating file handle"); g_free (filename); - + close (fd); + return(INVALID_HANDLE_VALUE); } @@ -1565,11 +1636,12 @@ gpointer CreateFile(const gunichar2 *name, guint32 fileaccess, if(ok==FALSE) { g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up file handle %p", handle); + close (fd); goto cleanup; } cf_ret = handle; - file_private_handle->fd=ret; + file_private_handle->fd=fd; file_private_handle->assigned=TRUE; file_private_handle->async = ((attrs & FILE_FLAG_OVERLAPPED) != 0); file_handle->filename=_wapi_handle_scratch_store (filename, @@ -1582,6 +1654,8 @@ gpointer CreateFile(const gunichar2 *name, guint32 fileaccess, file_handle->fileaccess=fileaccess; file_handle->sharemode=sharemode; file_handle->attrs=attrs; + file_handle->device = statbuf.st_dev; + file_handle->inode = statbuf.st_ino; #ifdef DEBUG g_message(G_GNUC_PRETTY_FUNCTION @@ -1873,7 +1947,7 @@ static gpointer stdhandle_create (int fd, const guchar *name) do { flags=fcntl(fd, F_GETFL); } - while (ret==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending()); + while (flags==-1 && errno==EINTR && !_wapi_thread_cur_apc_pending()); if(flags==-1) { /* Invalid fd. Not really much point checking for EBADF