This patch provides the ability to hook some files access
and display what we want instead.

Signed-off-by: Daniel Lezcano <daniel.lezcano@free.fr>
---
 procfs.c |  246 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 240 insertions(+), 6 deletions(-)

Index: lxcfs/procfs.c
===================================================================
--- lxcfs.orig/procfs.c
+++ lxcfs/procfs.c
@@ -17,31 +17,63 @@
 
 enum {
 	PROCFS_PROXY,
+	PROCFS_MIRROR,
 };
 
 struct procfs_info {
 	DIR *procdir;
 	DIR *subdir;
 	struct hsearch_data shadow_hash;
+	struct hsearch_data mirror_hash;
+};
+
+struct mirror_ops {
+	int (*open)(const char *, struct fuse_file_info *);
+	int (*read)(const char *, char *, size_t, off_t, struct fuse_file_info *);
+	int (*write)(const char *, const char *, size_t, off_t, struct fuse_file_info *);
+	int (*release)(const char *, struct fuse_file_info *);
+};
+
+static int meminfo_open(const char *, struct fuse_file_info *);
+static int meminfo_read(const char *, char *, size_t, off_t, struct fuse_file_info *);
+static int meminfo_release(const char *, struct fuse_file_info *);
+
+static struct mirror_ops meminfo_ops = {
+	.open    = meminfo_open,
+	.read    = meminfo_read,
+	.release = meminfo_release,
 };
 
 struct proxy_file {
 	int fd;
 };
 
+struct mirror_file {
+	struct mirror_ops *ops;
+	void *data;
+};
+
 struct procfs_file {
 	int type;
 	union {
 		struct proxy_file proxy;
+		struct mirror_file mirror;
 	} file;
+
+	void *private_data;
 };
 
 static ENTRY shadow_entries[] = {
 	{ "sys", NULL },
 };
 
+static ENTRY mirror_entries[] = {
+	{ "meminfo", &meminfo_ops },
+};
+
 #define ARRAY_SIZE(t) (sizeof(t) / sizeof(t[0]))
 #define SHADOW_HASH_SIZE 32
+#define MIRROR_HASH_SIZE 32
 
 ENTRY *hash_find(char *key, struct hsearch_data *hash)
 {
@@ -78,6 +110,8 @@ static int procfs_getattr(const char *pa
 	struct fuse_context *context = fuse_get_context();
 	struct procfs_info *fsinfo = context->private_data;
 	char *bname = strchr(path, '/');
+	struct mirror_ops *ops;
+	ENTRY *entry;
 
 	if (strcmp(path, "/"))
 		bname += 1;
@@ -92,8 +126,9 @@ static int procfs_open(const char *path,
 {
 	struct fuse_context *context = fuse_get_context();
 	struct procfs_info *fsinfo = context->private_data;
-	char *bname = strchr(path, '/');
 	struct procfs_file *pfile;
+	char *bname = strchr(path, '/');
+	ENTRY *entry;
 
 	if (strcmp(path, "/"))
 		bname += 1;
@@ -105,6 +140,16 @@ static int procfs_open(const char *path,
 	if (!pfile)
 		return -ENOMEM;
 
+	fi->fh = (typeof(fi->fh))pfile;
+
+	entry = hash_find(bname, &fsinfo->mirror_hash);
+	if (entry) {
+		pfile->type = PROCFS_MIRROR;
+		pfile->file.mirror.ops = entry->data;
+		if (pfile->file.mirror.ops->open)
+			return pfile->file.mirror.ops->open(path, fi);
+	}
+
 	pfile->type = PROCFS_PROXY;
 	pfile->file.proxy.fd = openat(dirfd(fsinfo->procdir), bname, fi->flags);
 	if (pfile->file.proxy.fd < 0) {
@@ -112,8 +157,6 @@ static int procfs_open(const char *path,
 		return -errno;
 	}
 
-	fi->fh = (typeof(fi->fh))pfile;
-
 	return 0;
 }
 
@@ -121,10 +164,14 @@ static int procfs_read(const char *path,
 		      off_t offset, struct fuse_file_info *fi)
 {
 	int ret;
-	struct procfs_file *pfile = (typeof(pfile))fi->fh;;
+	struct procfs_file *pfile = (typeof(pfile))fi->fh;
 
 	switch (pfile->type) {
 
+	case PROCFS_MIRROR:
+		ret = pfile->file.mirror.ops->read(path, buf, size, offset, fi);
+		break;
+
 	case PROCFS_PROXY:
 		ret = read(pfile->file.proxy.fd, buf, size);
 		if (ret < 0)
@@ -143,6 +190,10 @@ static int procfs_write(const char *path
 
 	switch (pfile->type) {
 
+	case PROCFS_MIRROR:
+		ret = pfile->file.mirror.ops->write(path, buf, size, offset, fi);
+		break;
+
 	case PROCFS_PROXY:
 		ret = write(pfile->file.proxy.fd, buf, size);
 		if (ret < 0)
@@ -156,16 +207,25 @@ static int procfs_write(const char *path
 static int procfs_release(const char *path, struct fuse_file_info *fi)
 {
 	struct procfs_file *pfile = (typeof(pfile))fi->fh;;
+	int ret;
 
 	switch (pfile->type) {
 
+	case PROCFS_MIRROR:
+		ret = pfile->file.mirror.ops->release(path, fi);
+		break;
+
 	case PROCFS_PROXY:
 		if (close(pfile->file.proxy.fd))
-			return -errno;
+			ret = -errno;
 		break;
+	default:
+		ret = -EIO;
 	}
 
-	return 0;
+	free(pfile);
+
+	return ret;
 }
 
 static int procfs_opendir(const char *path, struct fuse_file_info *fi)
@@ -265,9 +325,15 @@ static void *procfs_init(struct fuse_con
 			     shadow_entries, ARRAY_SIZE(shadow_entries)))
 		goto out_free_fsinfo;
 
+	if (procfs_init_hash(&fsinfo->mirror_hash, MIRROR_HASH_SIZE,
+			     mirror_entries, ARRAY_SIZE(mirror_entries)))
+		goto out_destroy_shadow_hash;
+
 out:
 	return fsinfo;
 
+out_destroy_shadow_hash:
+	hdestroy_r(&fsinfo->shadow_hash);
 out_free_fsinfo:
 	free(fsinfo);
 	fsinfo = NULL;
@@ -280,6 +346,7 @@ static void procfs_destroy(void *private
 
 	closedir(fsinfo->procdir);
 	hdestroy_r(&fsinfo->shadow_hash);
+	hdestroy_r(&fsinfo->mirror_hash);
 	free(fsinfo);
 }
 
@@ -301,3 +368,170 @@ int main(int argc, char *argv[])
 {
 	return fuse_main(argc, argv, &procfs_ops, NULL);
 }
+
+/*
+ * Mirroring specific functions
+ */
+
+static int get_cgroup_mount(const char *mtab, char *mnt)
+{
+        struct mntent *mntent;
+        FILE *file = NULL;
+        int err = -1;
+
+        file = setmntent(mtab, "r");
+        if (!file)
+                goto out;
+
+        while ((mntent = getmntent(file))) {
+
+		/* there is a cgroup mounted named "lxc" */
+		if (!strcmp(mntent->mnt_fsname, "lxc") &&
+		    !strcmp(mntent->mnt_type, "cgroup")) {
+			strcpy(mnt, mntent->mnt_dir);
+			err = 0;
+			break;
+		}
+
+		/* fallback to the first non-lxc cgroup found */
+                if (!strcmp(mntent->mnt_type, "cgroup") && err) {
+			strcpy(mnt, mntent->mnt_dir);
+			err = 0;
+		}
+        };
+
+        fclose(file);
+out:
+        return err;
+}
+
+static char *cgroup_name(void)
+{
+	char *path = "/proc/self/cgroup", *name;
+        char line[MAXPATHLEN];
+        FILE *file;
+
+        file = fopen(path, "r");
+        if (!file)
+                return NULL;
+        fscanf(file, "%s", line);
+	fclose(file);
+
+        strtok(line, ":");
+        strtok(NULL, ":");
+        name = strtok(NULL, ":");
+        name = strtok(name, "/");
+
+        return strdup(name);
+}
+
+struct meminfo {
+	FILE *memlimit;
+	FILE *memusage;
+	FILE *swaplimit;
+	FILE *swapusage;
+};
+
+static int meminfo_open(const char *path, struct fuse_file_info *fi)
+{
+	struct procfs_file *pfile = (typeof(pfile))fi->fh;
+	char cgmntpath[MAXPATHLEN];
+	char cgpath[MAXPATHLEN];
+	char *cgname;
+	struct meminfo *meminfo;
+	int ret;
+
+	if (get_cgroup_mount("/etc/mtab", cgmntpath))
+		if (get_cgroup_mount("/proc/mounts", cgmntpath))
+			return -ENOENT;
+
+	cgname = cgroup_name();
+	if (!cgname)
+		return -errno;
+
+	meminfo = malloc(sizeof(*meminfo));
+	if (!meminfo) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	pfile->file.mirror.data = meminfo;
+
+	ret = -ENOENT;
+
+	sprintf(cgpath, "%s/%s/memory.limit_in_bytes", cgmntpath, cgname);
+	meminfo->memlimit = fopen(cgpath, "r");
+	if (!meminfo->memlimit)
+		goto out_free_meminfo;
+
+	sprintf(cgpath, "%s/%s/memory.usage_in_bytes", cgmntpath, cgname);
+	meminfo->memusage = fopen(cgpath, "r");
+	if (!meminfo->memusage)
+		goto out_close_memlimit;
+
+	sprintf(cgpath, "%s/%s/memory.memsw.limit_in_bytes", cgmntpath, cgname);
+	meminfo->swaplimit = fopen(cgpath, "r");
+	if (!meminfo->swaplimit)
+		goto out_close_memusage;
+
+	sprintf(cgpath, "%s/%s/memory.memsw.limit_in_bytes", cgmntpath, cgname);
+	meminfo->swapusage = fopen(cgpath, "r");
+	if (!meminfo->swapusage)
+		goto out_close_swaplimit;
+
+	ret = 0;
+out:
+	free(cgname);
+	return ret;
+
+out_close_swaplimit:
+	fclose(meminfo->swaplimit);
+out_close_memusage:
+	fclose(meminfo->memusage);
+out_close_memlimit:
+	fclose(meminfo->memlimit);
+out_free_meminfo:
+	free(meminfo);
+	goto out;
+}
+
+static int meminfo_read(const char *path, char *buf, size_t size,
+			off_t offset, struct fuse_file_info *fi)
+{
+	struct procfs_file *pfile = (typeof(pfile))fi->fh;
+	struct meminfo *meminfo = pfile->file.mirror.data;
+	long long memlimit, memusage, swaplimit, swapusage;
+	size_t len;
+
+	if (offset)
+		return 0;
+
+	fscanf(meminfo->memlimit, "%lld", &memlimit);
+	fscanf(meminfo->memusage, "%lld", &memusage);
+	fscanf(meminfo->swaplimit, "%lld", &swaplimit);
+	fscanf(meminfo->swapusage, "%lld", &swapusage);
+
+	len = sprintf(buf, "MemTotal:       %8lu kB\n",
+		      memlimit >> 10);
+	len += sprintf(buf + len, "MemFree:        %8lu kB\n",
+		       (memlimit - memusage) >> 10);
+	len += sprintf(buf + len, "SwapTotal:      %8lu kB\n",
+		       swaplimit >> 10);
+	len += sprintf(buf + len, "SwapFree:       %8lu kB\n\n",
+		       (swaplimit - swapusage) >> 10);
+
+	return len;
+}
+
+static int meminfo_release(const char *path, struct fuse_file_info *fi)
+{
+	struct procfs_file *pfile = (typeof(pfile))fi->fh;
+	struct meminfo *meminfo = pfile->file.mirror.data;
+
+	fclose(meminfo->memlimit);
+	fclose(meminfo->memusage);
+	fclose(meminfo->swaplimit);
+	fclose(meminfo->swapusage);
+	free(meminfo);
+
+	return 0;
+}



