+2004-05-21 Dick Porter <dick@ximian.com>
+
+ * 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 <lluis@ximian.com>
* daemon-messages.c: Retry if the communication with the daemon is
WapiHandleRequestType_Scratch,
WapiHandleRequestType_ScratchFree,
WapiHandleRequestType_ProcessFork,
- WapiHandleRequestType_ProcessKill
+ WapiHandleRequestType_ProcessKill,
+ WapiHandleRequestType_GetOrSetShare,
+ WapiHandleRequestType_SetShare
} WapiHandleRequestType;
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;
WapiHandleRequest_ScratchFree scratch_free;
WapiHandleRequest_ProcessFork process_fork;
WapiHandleRequest_ProcessKill process_kill;
+ WapiHandleRequest_GetOrSetShare get_or_set_share;
+ WapiHandleRequest_SetShare set_share;
} u;
} WapiHandleRequest;
WapiHandleResponseType_Scratch,
WapiHandleResponseType_ScratchFree,
WapiHandleResponseType_ProcessFork,
- WapiHandleResponseType_ProcessKill
+ WapiHandleResponseType_ProcessKill,
+ WapiHandleResponseType_GetOrSetShare,
+ WapiHandleResponseType_SetShare
} WapiHandleResponseType;
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;
WapiHandleResponse_ScratchFree scratch_free;
WapiHandleResponse_ProcessFork process_fork;
WapiHandleResponse_ProcessKill process_kill;
+ WapiHandleResponse_GetOrSetShare get_or_set_share;
+ WapiHandleResponse_SetShare set_share;
} u;
} WapiHandleResponse;
/* 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.
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)
{
"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);
}
}
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]);
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));
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) {
}
}
+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;
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
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:
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)
{
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 ();
+ }
+}
guint32 fileaccess;
guint32 sharemode;
guint32 attrs;
+ dev_t device;
+ ino_t inode;
};
/* The boolean is for distinguishing between a zeroed struct being not
#include <mono/io-layer/handles-private.h>
#include <mono/io-layer/io-private.h>
#include <mono/io-layer/timefuncs-private.h>
+#include <mono/io-layer/thread-private.h>
#include <mono/utils/strenc.h>
#undef DEBUG
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");
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
* (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));
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);
}
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,
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
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