1 
2 /*
3     FUNIONFS: UNIONFS over FUSE Usermode filesystem
4     Copyright (C) 2005-2006  Stephane APIOU <stephane.apiou@free.fr>
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 
20 */
21 
22 #include <fuse.h>
23 #include <ctype.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <sys/mman.h>
31 #include <dirent.h>
32 
33 #include "funionfs.h"
34 
35 extern f_opt_t f_opt;
36 extern FILE *log_fd;
37 
38 extern struct unionfs_desc *unionfs_list;
39 
40 extern char initial_working_dir[PATH_MAX + 1];
41 
42 f_cbuf_t f_cbuf = { "", 0, 0 };
43 
44 int
funionfs_relist(void)45 funionfs_relist(void)
46 {
47 	int ret;
48 
49 	if (*f_opt.firstdir != '\0')
50 	{
51 		if (strcmp(f_opt.firstdir, "NONE")
52 		    && strcmp(f_opt.firstdir, "none")
53 		    && strcmp(f_opt.firstdir, "None"))
54 		{
55 			int fd;
56 			char *s, *firstdir;
57 			struct unionfs_desc *walk;
58 
59 			if (*f_opt.firstdir == '/')
60 				firstdir = strdup(f_opt.firstdir);
61 			else
62 				firstdir =
63 					rel2abspath(f_opt.firstdir,
64 						    initial_working_dir);
65 			DEBUG_PRINTF("firstdir=%s\n", firstdir);
66 			s = (char *) malloc(strlen(firstdir) +
67 					    strlen(CONTROL_FILE) + 2);
68 			strcpy(s, firstdir);
69 			strcat(s, "/");
70 			strcat(s, CONTROL_FILE);
71 			DEBUG_PRINTF("s=%s\n", s);
72 			free(firstdir);
73 			if (!s)
74 				return -ENOMEM;
75 			ret = unlink(s);
76 			if ((ret < 0) && (errno != ENOENT))
77 			{
78 				free(s);
79 				return -errno;
80 			}
81 			fd = open(s, O_CREAT | O_EXCL | O_WRONLY, 0600);
82 			free(s);
83 			if (fd < 0)
84 				return -errno;
85 			DEBUG_PRINTF("Starting to walk list...\n");
86 			walk = unionfs_list;
87 			while (walk)
88 			{
89 				ret = write(fd, walk->path, strlen(walk->path));
90 				if (ret < 0)
91 					return -errno;
92 				if (walk->ro)
93 					ret = write(fd, "=ro", 3);
94 				else
95 					ret = write(fd, "=rw", 3);
96 				if (ret < 0)
97 					return -errno;
98 				ret = write(fd, "\n", 1);
99 				if (ret < 0)
100 					return -errno;
101 				walk = walk->pnext;
102 			}
103 			close(fd);
104 		}
105 	}
106 	return 0;
107 
108 }
109 
110 static int
command_is(const char * text,const char * word)111 command_is(const char *text, const char *word)
112 {
113 	int l;
114 
115 	l = strlen(word);
116 	return !strncmp(text, word, l) && (isspace(text[l]) || !text[l]);
117 }
118 
119 static int
funionfs_execute(const char * command)120 funionfs_execute(const char *command)
121 {
122 	int ret;
123 
124 	if (!*command)
125 		return 0;
126 
127 	LOG("execute %s\n", command);
128 
129 	if (command_is(command, "relist"))
130 		return funionfs_relist();
131 
132 	if (command_is(command, "umount"))
133 	{
134 		if (!fuse_get_context()->uid
135 		    || (fuse_get_context()->uid == getuid())
136 		    || (fuse_get_context()->uid == geteuid()))
137 		{
138 			if (*f_opt.mountpoint == '/')
139 				fuse_unmount(f_opt.mountpoint);
140 			else
141 				fuse_unmount(rel2abspath
142 					     (f_opt.mountpoint,
143 					      initial_working_dir));
144 		}
145 		return -1;
146 	}
147 	if (command_is(command, "add_ro") && command[6])
148 	{
149 		ret = funionfs_addpath(command + 7, 1);
150 		if (ret != 1)
151 			return ret;
152 		return funionfs_relist();
153 	}
154 	if (command_is(command, "add_rw") && command[6])
155 	{
156 		ret = funionfs_addpath(command + 7, 0);
157 		if (ret != 1)
158 			return ret;
159 		return funionfs_relist();
160 	}
161 	if (command_is(command, "make_ro") && command[7])
162 	{
163 		struct unionfs_desc *walk;
164 
165 		if (command[8] != '/')
166 			return -ENOENT;
167 
168 		walk = unionfs_list;
169 		while (walk)
170 		{
171 			if (!strcmp(command + 8, walk->path))
172 				walk->ro = 1;
173 			walk = walk->pnext;
174 		}
175 		return funionfs_relist();
176 	}
177 	return 0;
178 }
179 
180 int
add_to_control_buffer(f_cbuf_t * to,const char * buf,size_t size)181 add_to_control_buffer(f_cbuf_t * to, const char *buf, size_t size)
182 {
183 	LOG("add_to_control_buf:\n");
184 	if (to->length + size >= CONTROL_BUFFER_SIZE)
185 		return -ENOSPC;
186 	{
187 		unsigned i, j;
188 
189 		for (i = 0; i < size; i++)
190 		{
191 			j = (to->start + to->length + i) % CONTROL_BUFFER_SIZE;
192 			to->data[j] = buf[i];
193 			LOG("%c", buf[i]);
194 		}
195 		j = (to->start + to->length + size) % CONTROL_BUFFER_SIZE;
196 		to->data[j] = 0;
197 		LOG("\nend add_to_control_buf\n");
198 	}
199 	to->length += size;
200 	return 0;
201 }
202 
203 void
flush_control_buffer(f_cbuf_t * what)204 flush_control_buffer(f_cbuf_t * what)
205 {
206 	what->start = 0;
207 	what->length = 0;
208 	what->data[0] = 0;
209 }
210 
211 char *
take_control_buffer_line(f_cbuf_t * from)212 take_control_buffer_line(f_cbuf_t * from)
213 {
214 	char *head, *tail;
215 	long n;
216 
217 	n = 0;
218 	head = from->data + from->start;
219 	tail = head;
220 	while (*tail != 0 && *tail != 10)
221 	{
222 		tail++;
223 		n++;
224 		if (tail - from->data >= CONTROL_BUFFER_SIZE)
225 			tail = from->data;
226 	}
227 	*tail = 0;
228 	from->length -= n + 1;
229 	if (from->length < 0)
230 	{
231 		from->length = 0;
232 		from->data[from->start] = 0;
233 	}
234 	from->start = (from->start + n + 1) % CONTROL_BUFFER_SIZE;
235 	return strdup(head);
236 }
237 
238 static int
is_control_buffer_line(f_cbuf_t * from)239 is_control_buffer_line(f_cbuf_t * from)
240 {
241 	int n;
242 
243 	n = 0;
244 	while ((from->data[(from->start + n) % CONTROL_BUFFER_SIZE] != 10)
245 	       && (from->data[(from->start + n) % CONTROL_BUFFER_SIZE] != 10)
246 	       && (n < CONTROL_BUFFER_SIZE))
247 		n++;
248 	return (from->data[(from->start + n) % CONTROL_BUFFER_SIZE] == 10);
249 }
250 
251 
252 int
funionfs_control_write(const char * buf,size_t size)253 funionfs_control_write(const char *buf, size_t size)
254 {
255 	int ret;
256 	char *s;
257 
258 	ret = add_to_control_buffer(&f_cbuf, buf, size);
259 	if (ret)
260 		return ret;
261 	while (is_control_buffer_line(&f_cbuf))
262 	{
263 		s = take_control_buffer_line(&f_cbuf);
264 		funionfs_execute(s);
265 		free(s);
266 	}
267 	return size;
268 }
269