You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1444 lines
30 KiB
C

/*
* Copyright © 2007 David Reveman
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without
* fee, provided that the above copyright notice appear in all copies
* and that both that copyright notice and this permission notice
* appear in supporting documentation, and that the name of
* David Reveman not be used in advertising or publicity pertaining to
* distribution of the software without specific, written prior permission.
* David Reveman makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without express or
* implied warranty.
*
* DAVID REVEMAN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
* NO EVENT SHALL DAVID REVEMAN BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Author: David Reveman <davidr@novell.com>
*/
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <poll.h>
#include <signal.h>
#include <sys/mount.h>
#include <fuse.h>
#include <fuse_lowlevel.h>
#include <compiz-core.h>
static CompMetadata fuseMetadata;
#define FUSE_INODE_TYPE_ROOT (1 << 0)
#define FUSE_INODE_TYPE_PLUGIN (1 << 1)
#define FUSE_INODE_TYPE_SCREEN (1 << 2)
#define FUSE_INODE_TYPE_DISPLAY (1 << 3)
#define FUSE_INODE_TYPE_OPTION (1 << 4)
#define FUSE_INODE_TYPE_TYPE (1 << 5)
#define FUSE_INODE_TYPE_VALUE (1 << 6)
#define FUSE_INODE_TYPE_ITEM_COUNT (1 << 7)
#define FUSE_INODE_TYPE_ITEM_TYPE (1 << 8)
#define FUSE_INODE_TYPE_ITEMS (1 << 9)
#define FUSE_INODE_TYPE_ITEM_VALUE (1 << 10)
#define FUSE_INODE_TYPE_MIN (1 << 11)
#define FUSE_INODE_TYPE_MAX (1 << 12)
#define FUSE_INODE_TYPE_PRECISION (1 << 13)
#define DIR_MASK (FUSE_INODE_TYPE_ROOT | \
FUSE_INODE_TYPE_PLUGIN | \
FUSE_INODE_TYPE_SCREEN | \
FUSE_INODE_TYPE_DISPLAY | \
FUSE_INODE_TYPE_OPTION | \
FUSE_INODE_TYPE_ITEMS)
#define CONST_DIR_MASK (FUSE_INODE_TYPE_PLUGIN | \
FUSE_INODE_TYPE_SCREEN | \
FUSE_INODE_TYPE_DISPLAY | \
FUSE_INODE_TYPE_OPTION)
#define WRITE_MASK (FUSE_INODE_TYPE_VALUE | \
FUSE_INODE_TYPE_ITEM_VALUE)
#define FUSE_INODE_FLAG_TRUNC (1 << 0)
typedef struct _FuseInode {
struct _FuseInode *parent;
struct _FuseInode *child;
struct _FuseInode *sibling;
int type;
int flags;
fuse_ino_t ino;
char *name;
} FuseInode;
typedef struct _FuseWriteBuffer {
char *data;
int size;
Bool dirty;
} FuseWriteBuffer;
static int displayPrivateIndex;
#define FUSE_DISPLAY_OPTION_MOUNT_POINT 0
#define FUSE_DISPLAY_OPTION_NUM 1
typedef struct _FuseDisplay {
CompOption opt[FUSE_DISPLAY_OPTION_NUM];
struct fuse_session *session;
struct fuse_chan *channel;
char *mountPoint;
CompWatchFdHandle watchFdHandle;
char *buffer;
} FuseDisplay;
#define GET_FUSE_DISPLAY(d) \
((FuseDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
#define FUSE_DISPLAY(d) \
FuseDisplay *fd = GET_FUSE_DISPLAY (d)
#define NUM_OPTIONS(d) (sizeof ((d)->opt) / sizeof (CompOption))
static fuse_ino_t nextIno = 1;
static FuseInode *inodes = NULL;
static FuseInode *
fuseAddInode (FuseInode *parent,
int type,
const char *name)
{
FuseInode *inode;
inode = malloc (sizeof (FuseInode));
if (!inode)
return NULL;
inode->parent = parent;
inode->sibling = NULL;
inode->child = NULL;
inode->type = type;
inode->flags = 0;
inode->ino = nextIno++;
inode->name = strdup (name);
if (parent)
{
if (parent->child)
inode->sibling = parent->child;
parent->child = inode;
}
return inode;
}
static void
fuseRemoveInode (FuseInode *parent,
FuseInode *inode)
{
while (inode->child)
fuseRemoveInode (inode, inode->child);
if (parent)
{
FuseInode *s, *prev = NULL;
for (s = parent->child; s; s = s->sibling)
{
if (s == inode)
break;
prev = s;
}
if (prev)
prev->sibling = inode->sibling;
else
parent->child = NULL;
}
if (inode->name)
free (inode->name);
free (inode);
}
static FuseInode *
fuseFindInode (FuseInode *inode,
fuse_ino_t ino,
int mask)
{
if (inode->ino != ino)
{
FuseInode *c = inode->child;
inode = NULL;
while (c)
{
inode = fuseFindInode (c, ino, ~0);
if (inode)
break;
c = c->sibling;
}
}
if (inode && (inode->type & mask))
return inode;
return NULL;
}
static FuseInode *
fuseLookupChild (FuseInode *inode,
const char *name)
{
FuseInode *c;
for (c = inode->child; c; c = c->sibling)
if (strcmp (c->name, name) == 0)
return c;
return NULL;
}
/* MULTIDPYERROR: only works with one or less displays present */
/* OBJECTOPTION: only display and screen options are supported */
static CompObject *
fuseGetObjectFromInode (FuseInode *inode)
{
CompObject *object;
object = compObjectFind (&core.base, COMP_OBJECT_TYPE_DISPLAY, NULL);
if (!object)
return NULL;
if (inode->type & FUSE_INODE_TYPE_SCREEN)
{
return compObjectFind (object, COMP_OBJECT_TYPE_SCREEN,
inode->name + 6);
}
else if (inode->type & FUSE_INODE_TYPE_DISPLAY)
{
return object;
}
return NULL;
}
static CompOption *
fuseGetOptionsFromInode (CompObject *object,
FuseInode *inode,
int *nOption)
{
CompOption *option = NULL;
if (inode->type & FUSE_INODE_TYPE_PLUGIN)
{
CompPlugin *p;
p = findActivePlugin (inode->name);
if (p && p->vTable->getObjectOptions)
option = (*p->vTable->getObjectOptions) (p, object, nOption);
}
return option;
}
static CompOption *
fuseGetOptionFromInode (FuseInode *inode)
{
if (inode->type & (FUSE_INODE_TYPE_OPTION |
FUSE_INODE_TYPE_ITEMS))
{
CompObject *object;
CompOption *option;
int nOption;
if (inode->type & FUSE_INODE_TYPE_ITEMS)
inode = inode->parent;
object = fuseGetObjectFromInode (inode);
if (!object)
return NULL;
option = fuseGetOptionsFromInode (object, inode->parent, &nOption);
if (option)
{
while (nOption--)
{
if (strcmp (inode->name, option->name) == 0)
return option;
option++;
}
}
}
return NULL;
}
/* MULTIDPYERROR: only works with one or less displays present */
static char *
fuseGetStringFromInode (FuseInode *inode)
{
CompOption *option;
char str[256];
if (!inode->parent)
return NULL;
option = fuseGetOptionFromInode (inode->parent);
if (!option)
return NULL;
if (inode->flags & FUSE_INODE_FLAG_TRUNC)
return strdup ("");
if (inode->type & FUSE_INODE_TYPE_TYPE)
{
return strdup (optionTypeToString (option->type));
}
else if (inode->type & (FUSE_INODE_TYPE_VALUE | FUSE_INODE_TYPE_ITEM_VALUE))
{
CompOptionValue *value = NULL;
CompOptionType type;
if (inode->type & FUSE_INODE_TYPE_ITEM_VALUE)
{
int i;
if (sscanf (inode->name, "value%d", &i))
{
if (i < option->value.list.nValue)
{
value = &option->value.list.value[i];
type = option->value.list.type;
}
}
}
else
{
value = &option->value;
type = option->type;
}
if (value)
{
switch (type) {
case CompOptionTypeBool:
return strdup (value->b ? "true" : "false");
case CompOptionTypeInt:
snprintf (str, 256, "%d", value->i);
return strdup (str);
case CompOptionTypeFloat:
snprintf (str, 256, "%f", value->f);
return strdup (str);
case CompOptionTypeString:
return strdup (value->s);
case CompOptionTypeColor:
return colorToString (value->c);
case CompOptionTypeKey:
if (core.displays)
return keyActionToString (core.displays, &value->action);
case CompOptionTypeButton:
if (core.displays)
return buttonActionToString (core.displays,
&value->action);
case CompOptionTypeEdge:
return edgeMaskToString (value->action.edgeMask);
case CompOptionTypeBell:
return strdup (value->action.bell ? "true" : "false");
case CompOptionTypeMatch:
return matchToString (&value->match);
default:
break;
}
}
}
else if (inode->type & FUSE_INODE_TYPE_MIN)
{
if (option->type == CompOptionTypeInt)
snprintf (str, 256, "%d", option->rest.i.min);
else
snprintf (str, 256, "%f", option->rest.f.min);
return strdup (str);
}
else if (inode->type & FUSE_INODE_TYPE_MAX)
{
if (option->type == CompOptionTypeInt)
snprintf (str, 256, "%d", option->rest.i.max);
else
snprintf (str, 256, "%f", option->rest.f.max);
return strdup (str);
}
else if (inode->type & FUSE_INODE_TYPE_PRECISION)
{
snprintf (str, 256, "%f", option->rest.f.precision);
return strdup (str);
}
else if (inode->type & FUSE_INODE_TYPE_ITEM_COUNT)
{
snprintf (str, 256, "%d", option->value.list.nValue);
return strdup (str);
}
else if (inode->type & FUSE_INODE_TYPE_ITEM_TYPE)
{
return strdup (optionTypeToString (option->value.list.type));
}
return NULL;
}
static void
fuseUpdateInode (CompDisplay *d,
FuseInode *inode)
{
CompScreen *s;
CompPlugin *p;
CompOption *option;
char str[256];
if (inode->type & FUSE_INODE_TYPE_ROOT)
{
FuseInode *c;
for (c = inode->child; c; c = c->sibling)
{
if (!findActivePlugin (c->name))
fuseRemoveInode (inode, c);
}
for (p = getPlugins (); p; p = p->next)
if (!fuseLookupChild (inode, p->vTable->name))
fuseAddInode (inode, FUSE_INODE_TYPE_PLUGIN,
p->vTable->name);
}
else if (inode->type & FUSE_INODE_TYPE_PLUGIN)
{
int n;
if (fuseGetOptionsFromInode (&d->base, inode, &n))
fuseAddInode (inode, FUSE_INODE_TYPE_DISPLAY, "allscreens");
for (s = d->screens; s; s = s->next)
{
if (fuseGetOptionsFromInode (&s->base, inode, &n))
{
sprintf (str, "screen%d", s->screenNum);
fuseAddInode (inode, FUSE_INODE_TYPE_SCREEN, str);
}
}
}
else if (inode->type & (FUSE_INODE_TYPE_DISPLAY | FUSE_INODE_TYPE_SCREEN))
{
CompObject *object;
object = fuseGetObjectFromInode (inode);
if (object)
{
int nOption;
option = fuseGetOptionsFromInode (object, inode->parent, &nOption);
if (option)
{
while (nOption--)
{
fuseAddInode (inode, FUSE_INODE_TYPE_OPTION, option->name);
option++;
}
}
}
}
else if (inode->type & FUSE_INODE_TYPE_OPTION)
{
option = fuseGetOptionFromInode (inode);
if (option)
{
fuseAddInode (inode, FUSE_INODE_TYPE_TYPE, "type");
switch (option->type) {
case CompOptionTypeFloat:
fuseAddInode (inode, FUSE_INODE_TYPE_PRECISION, "precision");
/* fall-through */
case CompOptionTypeInt:
fuseAddInode (inode, FUSE_INODE_TYPE_MIN, "min");
fuseAddInode (inode, FUSE_INODE_TYPE_MAX, "max");
/* fall-through */
case CompOptionTypeBool:
case CompOptionTypeString:
case CompOptionTypeColor:
case CompOptionTypeKey:
case CompOptionTypeButton:
case CompOptionTypeEdge:
case CompOptionTypeBell:
case CompOptionTypeMatch:
fuseAddInode (inode, FUSE_INODE_TYPE_VALUE, "value");
break;
case CompOptionTypeList:
fuseAddInode (inode, FUSE_INODE_TYPE_ITEMS, "items");
fuseAddInode (inode, FUSE_INODE_TYPE_ITEM_COUNT,
"number_of_items");
fuseAddInode (inode, FUSE_INODE_TYPE_ITEM_TYPE, "item_type");
default:
break;
}
}
}
else if (inode->type & FUSE_INODE_TYPE_ITEMS)
{
option = fuseGetOptionFromInode (inode->parent);
if (option && option->type == CompOptionTypeList)
{
FuseInode *c, *next;
int i, nValue = option->value.list.nValue;
for (i = 0; i < option->value.list.nValue; i++)
{
sprintf (str, "value%d", i);
if (!fuseLookupChild (inode, str))
fuseAddInode (inode, FUSE_INODE_TYPE_ITEM_VALUE, str);
}
for (c = inode->child; c; c = next)
{
next = c->sibling;
if (sscanf (c->name, "value%d", &i) == 0 || i >= nValue)
fuseRemoveInode (inode, c);
}
}
}
}
static void
fuseInodeStat (CompDisplay *d,
FuseInode *inode,
struct stat *stbuf)
{
stbuf->st_ino = inode->ino;
if (inode->type & DIR_MASK)
{
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
}
else
{
char *str;
if (inode->type & WRITE_MASK)
stbuf->st_mode = S_IFREG | 0666;
else
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = 0;
str = fuseGetStringFromInode (inode);
if (str)
{
stbuf->st_size = strlen (str);
free (str);
}
}
}
static Bool
fuseInitValue (CompOptionValue *value,
CompOptionType type,
CompOptionValue *src)
{
switch (type) {
case CompOptionTypeBool:
value->b = src->b;
break;
case CompOptionTypeInt:
value->i = src->i;
break;
case CompOptionTypeFloat:
value->f = src->f;
break;
case CompOptionTypeString:
value->s = strdup (src->s);
break;
case CompOptionTypeColor:
memcpy (value->c, src->c, sizeof (*src->c));
break;
case CompOptionTypeKey:
case CompOptionTypeButton:
case CompOptionTypeEdge:
case CompOptionTypeBell:
value->action = src->action;
break;
case CompOptionTypeMatch:
matchInit (&value->match);
matchCopy (&value->match, &src->match);
break;
default:
return FALSE;
}
return TRUE;
}
static Bool
fuseInitValueFromString (CompObject *object,
CompOptionValue *value,
CompOptionType type,
char *str)
{
switch (type) {
case CompOptionTypeBool:
value->b = strcmp (str, "true") ? FALSE : TRUE;
break;
case CompOptionTypeInt:
value->i = atoi (str);
break;
case CompOptionTypeFloat:
value->f = strtod (str, NULL);
break;
case CompOptionTypeString:
value->s = strdup (str);
break;
case CompOptionTypeColor:
if (!stringToColor (str, value->c))
return FALSE;
break;
case CompOptionTypeKey:
while (object && object->type != COMP_OBJECT_TYPE_DISPLAY)
object = object->parent;
if (!object)
return FALSE;
stringToKeyAction (GET_CORE_DISPLAY (object), str, &value->action);
break;
case CompOptionTypeButton:
while (object && object->type != COMP_OBJECT_TYPE_DISPLAY)
object = object->parent;
if (!object)
return FALSE;
stringToButtonAction (GET_CORE_DISPLAY (object), str, &value->action);
break;
case CompOptionTypeEdge:
value->action.edgeMask = stringToEdgeMask (str);
break;
case CompOptionTypeBell:
value->action.bell = strcmp (str, "true") ? FALSE : TRUE;
break;
case CompOptionTypeMatch:
matchInit (&value->match);
matchAddFromString (&value->match, str);
break;
default:
return FALSE;
}
return TRUE;
}
static void
fuseSetInodeOptionUsingString (FuseInode *inode,
char *str)
{
CompOption *option;
option = fuseGetOptionFromInode (inode->parent);
if (option)
{
CompOptionValue value;
CompObject *object;
const char *pluginName;
if (inode->type & FUSE_INODE_TYPE_VALUE)
{
object = fuseGetObjectFromInode (inode->parent->parent);
if (!object)
return;
if (!fuseInitValueFromString (object, &value, option->type, str))
return;
pluginName = inode->parent->parent->parent->name;
}
else if (inode->type & FUSE_INODE_TYPE_ITEM_VALUE)
{
int i, item, nValue = option->value.list.nValue;
if (!sscanf (inode->name, "value%d", &item))
return;
if (item >= nValue)
return;
object = fuseGetObjectFromInode (inode->parent->parent->parent);
if (!object)
return;
value.list.value = malloc (sizeof (CompOptionValue) * nValue);
if (!value.list.value)
return;
value.list.type = option->value.list.type;
value.list.nValue = 0;
for (i = 0; i < nValue; i++)
{
if (i == item)
{
if (!fuseInitValueFromString (object,
&value.list.value[i],
value.list.type,
str))
break;
}
else
{
if (!fuseInitValue (&value.list.value[i],
value.list.type,
&option->value.list.value[i]))
break;
}
value.list.nValue++;
}
/* failed */
if (value.list.nValue < nValue)
{
compFiniOptionValue (&value, option->type);
return;
}
pluginName = inode->parent->parent->parent->parent->name;
}
else
{
return;
}
(*core.setOptionForPlugin) (object, pluginName, option->name, &value);
compFiniOptionValue (&value, option->type);
}
}
static void
compiz_getattr (fuse_req_t req,
fuse_ino_t ino,
struct fuse_file_info *fi)
{
CompDisplay *d = (CompDisplay *) fuse_req_userdata (req);
FuseInode *inode;
inode = fuseFindInode (inodes, ino, ~0);
if (inode)
{
struct stat stbuf;
memset (&stbuf, 0, sizeof (stbuf));
fuseInodeStat (d, inode, &stbuf);
fuse_reply_attr (req, &stbuf, 1.0);
}
else
{
fuse_reply_err (req, ENOENT);
}
}
static void
compiz_setattr (fuse_req_t req,
fuse_ino_t ino,
struct stat *attr,
int to_set,
struct fuse_file_info *fi)
{
CompDisplay *d = (CompDisplay *) fuse_req_userdata (req);
FuseInode *inode;
inode = fuseFindInode (inodes, ino, WRITE_MASK);
if (inode)
{
struct stat stbuf;
if ((to_set & FUSE_SET_ATTR_SIZE) != FUSE_SET_ATTR_SIZE)
{
fuse_reply_err (req, EACCES);
return;
}
if (attr->st_size != 0)
{
fuse_reply_err (req, EACCES);
return;
}
inode->flags |= FUSE_INODE_FLAG_TRUNC;
memset (&stbuf, 0, sizeof (stbuf));
fuseInodeStat (d, inode, &stbuf);
fuse_reply_attr (req, &stbuf, 1.0);
}
else
{
fuse_reply_err (req, ENOENT);
}
}
static void
compiz_lookup (fuse_req_t req,
fuse_ino_t parent,
const char *name)
{
CompDisplay *d = (CompDisplay *) fuse_req_userdata (req);
FuseInode *inode;
struct fuse_entry_param e;
inode = fuseFindInode (inodes, parent, DIR_MASK);
if (!inode)
{
fuse_reply_err (req, ENOENT);
return;
}
if (!inode->child || !(inode->type & CONST_DIR_MASK))
fuseUpdateInode (d, inode);
inode = fuseLookupChild (inode, name);
if (!inode)
{
fuse_reply_err (req, ENOENT);
return;
}
memset (&e, 0, sizeof (e));
e.attr_timeout = 1.0;
e.entry_timeout = 1.0;
e.ino = inode->ino;
fuseInodeStat (d, inode, &e.attr);
fuse_reply_entry (req, &e);
}
struct dirbuf {
char *p;
size_t size;
};
static void
dirbuf_add (fuse_req_t req,
struct dirbuf *b,
const char *name,
fuse_ino_t ino)
{
struct stat stbuf;
size_t oldSize = b->size;
b->size += fuse_add_direntry (req, NULL, 0, name, NULL, 0);
b->p = (char *) realloc (b->p, b->size);
memset (&stbuf, 0, sizeof (stbuf));
stbuf.st_ino = ino;
fuse_add_direntry (req, b->p + oldSize, b->size - oldSize, name, &stbuf,
b->size);
}
static int
reply_buf_limited (fuse_req_t req,
const char *buf,
size_t bufsize,
off_t off,
size_t maxsize)
{
if (off < bufsize)
return fuse_reply_buf (req, buf + off, MIN (bufsize - off, maxsize));
else
return fuse_reply_buf (req, NULL, 0);
}
static void
compiz_readdir (fuse_req_t req,
fuse_ino_t ino,
size_t size,
off_t off,
struct fuse_file_info *fi)
{
CompDisplay *d = (CompDisplay *) fuse_req_userdata (req);
FuseInode *inode, *c;
struct dirbuf b;
inode = fuseFindInode (inodes, ino, DIR_MASK);
if (!inode)
{
fuse_reply_err (req, ENOTDIR);
return;
}
memset (&b, 0, sizeof (b));
dirbuf_add (req, &b, ".", ino);
dirbuf_add (req, &b, "..", inode->parent ? inode->parent->ino : ino);
if (!inode->child || !(inode->type & CONST_DIR_MASK))
fuseUpdateInode (d, inode);
for (c = inode->child; c; c = c->sibling)
dirbuf_add (req, &b, c->name, c->ino);
reply_buf_limited (req, b.p, b.size, off, size);
free (b.p);
}
static void
compiz_open (fuse_req_t req,
fuse_ino_t ino,
struct fuse_file_info *fi)
{
FuseInode *inode;
inode = fuseFindInode (inodes, ino, ~0);
if (!inode)
{
fuse_reply_err (req, ENOENT);
return;
}
fi->fh = 0;
if (inode->type & DIR_MASK)
{
fuse_reply_err (req, EISDIR);
}
else if (inode->type & WRITE_MASK)
{
if ((fi->flags & 3) != O_RDONLY)
{
char *data;
if (fi->flags & O_TRUNC)
data = strdup ("");
else
data = fuseGetStringFromInode (inode);
if (data)
{
FuseWriteBuffer *wb;
wb = malloc (sizeof (FuseWriteBuffer));
if (wb)
{
wb->data = data;
wb->size = strlen (wb->data);
wb->dirty = TRUE;
fi->fh = (unsigned long) wb;
}
else
{
free (data);
}
}
}
fuse_reply_open (req, fi);
}
else if ((fi->flags & 3) != O_RDONLY)
{
fuse_reply_err (req, EACCES);
}
else
{
fuse_reply_open (req, fi);
}
}
static void
compiz_read (fuse_req_t req,
fuse_ino_t ino,
size_t size,
off_t off,
struct fuse_file_info *fi)
{
FuseInode *inode;
char *str = NULL;
inode = fuseFindInode (inodes, ino, ~0);
if (inode)
str = fuseGetStringFromInode (inode);
if (str)
{
reply_buf_limited (req, str, strlen (str), off, size);
free (str);
}
else
{
reply_buf_limited (req, NULL, 0, off, size);
}
}
static void
compiz_write (fuse_req_t req,
fuse_ino_t ino,
const char *buf,
size_t size,
off_t off,
struct fuse_file_info *fi)
{
FuseInode *inode;
inode = fuseFindInode (inodes, ino, WRITE_MASK);
if (inode && fi->fh)
{
FuseWriteBuffer *wb = (FuseWriteBuffer *) (uintptr_t) fi->fh;
if (off + size > wb->size)
{
char *data;
data = realloc (wb->data, off + size + 1);
if (!data)
{
fuse_reply_err (req, ENOBUFS);
return;
}
data[off + size] = '\0';
wb->data = data;
wb->size = off + size;
}
memcpy (wb->data + off, buf, size);
wb->dirty = TRUE;
fuse_reply_write (req, size);
}
else
{
fuse_reply_err (req, ENOENT);
}
}
static void
compiz_release (fuse_req_t req,
fuse_ino_t ino,
struct fuse_file_info *fi)
{
if (fi->fh)
{
FuseWriteBuffer *wb = (FuseWriteBuffer *) (uintptr_t) fi->fh;
FuseInode *inode;
inode = fuseFindInode (inodes, ino, WRITE_MASK);
if (inode && wb->dirty)
{
fuseSetInodeOptionUsingString (inode, wb->data);
inode->flags &= ~FUSE_INODE_FLAG_TRUNC;
}
free (wb->data);
free (wb);
}
fuse_reply_err (req, 0);
}
static void
compiz_fsync (fuse_req_t req,
fuse_ino_t ino,
int datasync,
struct fuse_file_info *fi)
{
if (fi->fh)
{
FuseWriteBuffer *wb = (FuseWriteBuffer *) (uintptr_t) fi->fh;
FuseInode *inode;
inode = fuseFindInode (inodes, ino, WRITE_MASK);
if (inode && wb->dirty)
{
fuseSetInodeOptionUsingString (inode, wb->data);
inode->flags &= ~FUSE_INODE_FLAG_TRUNC;
wb->dirty = FALSE;
}
}
fuse_reply_err (req, 0);
}
static struct fuse_lowlevel_ops compiz_ll_oper = {
.lookup = compiz_lookup,
.getattr = compiz_getattr,
.setattr = compiz_setattr,
.readdir = compiz_readdir,
.open = compiz_open,
.read = compiz_read,
.write = compiz_write,
.release = compiz_release,
.fsync = compiz_fsync
};
static void
fuseUnmount (CompDisplay *d)
{
FUSE_DISPLAY (d);
if (fd->watchFdHandle)
{
compRemoveWatchFd (fd->watchFdHandle);
fd->watchFdHandle = 0;
}
if (fd->mountPoint)
{
/* unmount will destroy the channel */
fuse_unmount (fd->mountPoint, fd->channel);
free (fd->mountPoint);
fd->mountPoint = NULL;
fd->channel = NULL;
}
if (fd->buffer)
{
free (fd->buffer);
fd->buffer = NULL;
}
}
static Bool
fuseProcessMessages (void *data)
{
CompDisplay *d = (CompDisplay *) data;
struct fuse_chan *channel;
size_t bufferSize;
int res = 0;
FUSE_DISPLAY (d);
channel = fuse_session_next_chan (fd->session, NULL);
bufferSize = fuse_chan_bufsize (channel);
if (fuse_session_exited (fd->session))
return FALSE;
for (;;)
{
struct fuse_chan *tmpch = channel;
res = fuse_chan_recv (&tmpch, fd->buffer, bufferSize);
if (res == -EINTR)
continue;
if (res > 0)
fuse_session_process (fd->session, fd->buffer, res, tmpch);
break;
}
return TRUE;
}
static void
fuseMount (CompDisplay *d)
{
char *mountPoint;
struct fuse_args args = FUSE_ARGS_INIT (0, NULL);
FUSE_DISPLAY (d);
mountPoint = strdup (fd->opt[FUSE_DISPLAY_OPTION_MOUNT_POINT].value.s);
if (!mountPoint)
return;
fuse_opt_add_arg (&args, "");
fuse_opt_add_arg (&args, "-o");
fuse_opt_add_arg (&args, "allow_root");
fd->channel = fuse_mount (mountPoint, &args);
if (!fd->channel)
{
fuse_opt_free_args (&args);
free (mountPoint);
return;
}
fuse_opt_free_args (&args);
fd->buffer = malloc (fuse_chan_bufsize (fd->channel));
if (!fd->buffer)
{
fuse_unmount (mountPoint, fd->channel);
free (mountPoint);
fd->channel = NULL;
return;
}
fd->mountPoint = mountPoint;
fuse_session_add_chan (fd->session, fd->channel);
fd->watchFdHandle = compAddWatchFd (fuse_chan_fd (fd->channel),
POLLIN | POLLPRI | POLLHUP | POLLERR,
fuseProcessMessages,
d);
}
static CompOption *
fuseGetDisplayOptions (CompPlugin *plugin,
CompDisplay *display,
int *count)
{
FUSE_DISPLAY (display);
*count = NUM_OPTIONS (fd);
return fd->opt;
}
static Bool
fuseSetDisplayOption (CompPlugin *plugin,
CompDisplay *display,
const char *name,
CompOptionValue *value)
{
CompOption *o;
int index;
FUSE_DISPLAY (display);
o = compFindOption (fd->opt, NUM_OPTIONS (fd), name, &index);
if (!o)
return FALSE;
switch (index) {
case FUSE_DISPLAY_OPTION_MOUNT_POINT:
if (compSetStringOption (o, value))
{
fuseUnmount (display);
fuseMount (display);
return TRUE;
}
default:
break;
}
return FALSE;
}
static const CompMetadataOptionInfo fuseDisplayOptionInfo[] = {
{ "mount_point", "string", 0, 0, 0 }
};
static Bool
fuseInitDisplay (CompPlugin *p,
CompDisplay *d)
{
FuseDisplay *fd;
struct sigaction sa;
if (!checkPluginABI ("core", CORE_ABIVERSION))
return FALSE;
memset (&sa, 0, sizeof (struct sigaction));
sa.sa_handler = SIG_IGN;
sigemptyset (&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction (SIGPIPE, &sa, NULL) == -1)
return FALSE;
fd = malloc (sizeof (FuseDisplay));
if (!fd)
return FALSE;
if (!compInitDisplayOptionsFromMetadata (d,
&fuseMetadata,
fuseDisplayOptionInfo,
fd->opt,
FUSE_DISPLAY_OPTION_NUM))
{
free (fd);
return FALSE;
}
fd->session = fuse_lowlevel_new (NULL,
&compiz_ll_oper, sizeof (compiz_ll_oper),
(void *) d);
if (!fd->session)
{
compFiniDisplayOptions (d, fd->opt, FUSE_DISPLAY_OPTION_NUM);
free (fd);
return FALSE;
}
fd->watchFdHandle = 0;
fd->channel = NULL;
fd->buffer = NULL;
fd->mountPoint = NULL;
d->base.privates[displayPrivateIndex].ptr = fd;
fuseMount (d);
return TRUE;
}
static void
fuseFiniDisplay (CompPlugin *p,
CompDisplay *d)
{
FUSE_DISPLAY (d);
fuseUnmount (d);
fuse_session_destroy (fd->session);
compFiniDisplayOptions (d, fd->opt, FUSE_DISPLAY_OPTION_NUM);
free (fd);
}
static Bool
fuseInit (CompPlugin *p)
{
if (!compInitPluginMetadataFromInfo (&fuseMetadata,
p->vTable->name,
fuseDisplayOptionInfo,
FUSE_DISPLAY_OPTION_NUM,
0, 0))
return FALSE;
inodes = fuseAddInode (NULL, FUSE_INODE_TYPE_ROOT, ".");
if (!inodes)
{
compFiniMetadata (&fuseMetadata);
return FALSE;
}
displayPrivateIndex = allocateDisplayPrivateIndex ();
if (displayPrivateIndex < 0)
{
fuseRemoveInode (NULL, inodes);
compFiniMetadata (&fuseMetadata);
return FALSE;
}
compAddMetadataFromFile (&fuseMetadata, p->vTable->name);
return TRUE;
}
static CompBool
fuseInitObject (CompPlugin *p,
CompObject *o)
{
static InitPluginObjectProc dispTab[] = {
(InitPluginObjectProc) 0, /* InitCore */
(InitPluginObjectProc) fuseInitDisplay
};
RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
}
static void
fuseFiniObject (CompPlugin *p,
CompObject *o)
{
static FiniPluginObjectProc dispTab[] = {
(FiniPluginObjectProc) 0, /* FiniCore */
(FiniPluginObjectProc) fuseFiniDisplay
};
DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
}
static CompOption *
fuseGetObjectOptions (CompPlugin *plugin,
CompObject *object,
int *count)
{
static GetPluginObjectOptionsProc dispTab[] = {
(GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
(GetPluginObjectOptionsProc) fuseGetDisplayOptions
};
*count = 0;
RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
NULL, (plugin, object, count));
}
static CompBool
fuseSetObjectOption (CompPlugin *plugin,
CompObject *object,
const char *name,
CompOptionValue *value)
{
static SetPluginObjectOptionProc dispTab[] = {
(SetPluginObjectOptionProc) 0, /* SetCoreOption */
(SetPluginObjectOptionProc) fuseSetDisplayOption
};
RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
(plugin, object, name, value));
}
static void
fuseFini (CompPlugin *p)
{
fuseRemoveInode (NULL, inodes);
freeDisplayPrivateIndex (displayPrivateIndex);
compFiniMetadata (&fuseMetadata);
}
static CompMetadata *
fuseGetMetadata (CompPlugin *plugin)
{
return &fuseMetadata;
}
CompPluginVTable fuseVTable = {
"fs",
fuseGetMetadata,
fuseInit,
fuseFini,
fuseInitObject,
fuseFiniObject,
fuseGetObjectOptions,
fuseSetObjectOption
};
CompPluginVTable *
getCompPluginInfo20070830 (void)
{
return &fuseVTable;
}