1 /*
2 *
3 * Copyright (c) 2003 The Regents of the University of California. All
4 * rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * - Neither the name of the University nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
19 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
25 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 */
30
31 /*
32 * FUSDd
33 *
34 * The user-space component of FUSD for non-DevFS Linux 2.4 systems
35 *
36 */
37
38 #include <sys/stat.h>
39 #include <unistd.h>
40 #include <stdlib.h>
41 #include <sys/types.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <string.h>
45 #include <stdio.h>
46 #include <sys/ioctl.h>
47
48 #include "fusd.h"
49 #include "fusdd_i.h"
50 #include <fusd_msg.h>
51
52 #define FUSD_MAX_DEVS 20000
53 #define F_PATH_MAX (FUSD_MAX_NAME_LENGTH + 10)
54
55 extern int unlink_dirs;
56
57 struct dev {
58 char *name;
59 uint16_t major;
60 uint16_t minor;
61 int mark:1;
62 int del:1;
63 };
64
65 struct dev *dev_lookup(char *name);
66 void dev_insert(struct dev *add);
67 void dev_compress();
68 void dev_dump();
69
70 /* current active devices */
71 struct dev *active_devs=NULL;
72 int dev_count=0;
73 int dev_alloc=0;
74 int verbose=0;
75
76 #define DEV_TO_NUM(x) (makedev((x)->major, (x)->minor))
77 #define DEV_EQ(x,y) (((x)->major == (y)->major) && ((x)->minor == (y)->minor))
78 #define DEV_ZERO(x) (((x)->major == 0) && ((x)->minor == 0))
79
80 /*
81 * debug function
82 */
83
84 void dev_dump()
85 {
86 int i;
87 printf("%d devs, %d alloc\n", dev_count, dev_alloc);
88 for (i=0; i<dev_count; i++) {
89 printf("%s %d\n", active_devs[i].name, active_devs[i].del);
90 }
91 printf("\n");
92 }
93
94
95 void dev_test()
96 {
97 struct dev d = {};
98
99 #define TESTI(s) d.name = strdup(s); dev_insert(&d); dev_dump();
100 #define TESTD(s) dev_lookup(s)->del = 1; dev_dump();
101
102 TESTI("b");
103 TESTI("d");
104 TESTI("f");
105 TESTI("a");
106 TESTI("aa");
107 TESTI("g");
108
109 TESTD("d");
110 TESTD("a");
111 TESTD("g");
112 dev_compress(); dev_dump();
113
114 TESTD("f");
115 dev_compress(); dev_dump();
116 }
117
118 /*
119 * device list manipulation
120 */
121
122 int dev_cmp(const void *x, const void *y)
123 {
124 struct dev *xd = (struct dev *)x;
125 struct dev *yd = (struct dev *)y;
126 return strcmp(xd->name, yd->name);
127 }
128
129 struct dev *dev_lookup(char *name)
130 {
131 struct dev key = {
132 name: name
133 };
134 if (active_devs == NULL) return NULL;
135 return bsearch(&key, active_devs, dev_count, sizeof(struct dev), dev_cmp);
136 }
137
138 void dev_insert(struct dev *add)
139 {
140 int i;
141
142 dev_count++;
143
144 /* need to expand? */
145 if (dev_alloc < dev_count) {
146 dev_alloc *= 2;
147 if (dev_alloc == 0) dev_alloc = 64;
148 active_devs = realloc(active_devs, sizeof(struct dev)*dev_alloc);
149 }
150
151 /* locate position */
152 for (i=dev_count-1; i>0; i--) {
153 if (strcmp(add->name, active_devs[i-1].name) > 0) {
154 goto found;
155 }
156 active_devs[i] = active_devs[i-1];
157 }
158
159 found:
160 /* store */
161 active_devs[i] = *add;
162 }
163
164
165 void dev_compress()
166 {
167 int i;
168 int shift=0;
169
170 for (i=0; i<dev_count; i++) {
171 if (active_devs[i].del) {
172 free(active_devs[i].name);
173 shift++;
174 continue;
175 }
176 if (shift) active_devs[i-shift] = active_devs[i];
177 }
178 dev_count -= shift;
179 }
180
181
182 /*
183 * Prepend /dev.. careful.. static allocation!
184 */
185
186 char *dev_path(char *name)
187 {
188 static char path[F_PATH_MAX+1];
189
190 /* prepend /dev */
191 strcpy(path, DEFAULT_DEV_ROOT);
192 strcat(path, name);
193
194 return path;
195 }
196
197
198 /*
199 * mkdir_with_parents: given a pathname, create a directory with that
200 * name and possibly any parents of that directory, if necessary.
201 *
202 * Returns:
203 * 0 if the directory was created successfully (or already existed)
204 * -1 with errno set appropriately if there was an error creating the
205 * directory or any of its parents
206 */
207
208 int f_mkdir_with_parents(char *path_orig)
209 {
210 char path[F_PATH_MAX+1];
211 char *parent_end;
212
213 strcpy(path, path_orig);
214
215 /* skip over any leading slashes, then find the first slash after
216 * that */
217 parent_end = index(path + strspn(path, "/"), '/');
218
219 /* now, for each component of the path, create the dir */
220 while (parent_end != NULL) {
221 *parent_end = '\0';
222
223 if (mkdir(path, 0755) < 0 && errno != EEXIST) {
224 return -1;
225 }
226
227 *parent_end = '/';
228 parent_end = index(parent_end+1, '/');
229 }
230
231 return 0;
232 }
233
234
235
236 int clear_marks(void *data)
237 {
238 int i;
239 if (verbose) fprintf(stderr, "Clearing Marks\n");
240 /* clear the marks first */
241 for (i=0; i<dev_count; i++)
242 active_devs[i].mark=0;
243 return 0;
244 }
245
246
247 void dev_remove(struct dev *dev)
248 {
249 if (dev->name) {
250 if (verbose)
251 fprintf(stderr, "Unlinking unused device %s\n", dev->name);
252 unlink(dev_path(dev->name));
253
254 if (unlink_dirs) {
255 /* rmdir the enclosing directory */
256 char *tmp = strdup(dev->name);
257 char *slash;
258
259 while ((slash = strrchr(tmp, '/'))) {
260 *slash = 0;
261
262 if (verbose)
263 fprintf(stderr, "Removing dir %s\n", dev_path(tmp));
264
265 if (rmdir(dev_path(tmp)) < 0) {
266 if (verbose)
267 fprintf(stderr, "Unable to remove dir %s: %m\n", tmp);
268 break;
269 }
270 }
271
272 free(tmp);
273 }
274 }
275 dev->del = 1;
276 }
277
278
279 int unlink_unmarked(void *data)
280 {
281 int i;
282 if (verbose) fprintf(stderr, "Unlinking unmarked\n");
283 /* unlink any unmarked devices */
284 for (i=0; i<dev_count; i++) {
285 if (active_devs[i].mark == 0)
286 dev_remove(&active_devs[i]);
287 }
288 dev_compress();
289 return 0;
290 }
291
292
293 int process(fusd_status_t *fs, int count, void *data)
294 {
295 int i;
296
297 for (i=0; i<count; i++, fs++) {
298 struct stat s;
299 int fd;
300 struct dev *ptr = dev_lookup(fs->name);
301 char *path;
302
303 /* if zombie, remove if present */
304 if (fs->zombie) {
305 if (verbose)
306 fprintf(stderr, "ignoring zombie %s\n", fs->name);
307 continue;
308 }
309
310 /* if null device, remove if present */
311 if (DEV_ZERO(fs)) {
312 if (verbose)
313 fprintf(stderr, "ignoring non-device %s\n", fs->name);
314 continue;
315 }
316
317 /* if present, verify the device setting */
318 if (ptr) {
319 /* we're done if it matches out current state */
320 if (DEV_EQ(ptr,fs))
321 goto mark;
322 /* otherwise it will be updated after we succeed */
323 }
324
325 /*
326 * OK, now we have an entry that might need to be added
327 */
328
329 path = dev_path(fs->name);
330
331 if (verbose)
332 fprintf(stderr, "checking '%s' -> %d,%d [%x]\n",
333 path, fs->major, fs->minor, (uint32_t)DEV_TO_NUM(fs));
334
335 /* Stat the device file. */
336 if (stat(path, &s) < 0) {
337 if (errno == ENOENT) {
338
339 /* create any subdirectories */
340 if (f_mkdir_with_parents(path) < 0) {
341 fprintf(stderr, "Unable to create directory components for device %s\n", path);
342 continue;
343 }
344
345 /* do the mknod */
346 goto create;
347 }
348
349 fprintf(stderr, "Stat of %s failed: %m\n", path);
350 continue;
351 }
352
353 /* Check the stat, see if it matches */
354
355 /* if this is a regular file, delete it! */
356 if (S_ISREG(s.st_mode)) {
357 fprintf(stderr, "Deleting existing regular file: %s\n", path);
358 if (unlink(path) < 0) {
359 fprintf(stderr, "Unable to unlink existing regular file %s\n", path);
360 goto remove;
361 }
362 goto create;
363 }
364
365 /* if this is not a chrdev, ignore it -- (this may hang the client)
366 * this might be a directory!!
367 * $$$ figure out a better thing to do here?? */
368 if (!S_ISCHR(s.st_mode)) {
369
370 if (S_ISDIR(s.st_mode))
371 fprintf(stderr, "Device overloading a directory!! NOT deleting: %s\n", path);
372 else
373 fprintf(stderr, "Device overloading a non-regular file.. NOT deleting: %s\n", path);
374
375 /* don't goto remove here because we don't want to delete this file! */
376 if (ptr) ptr->del = 1;
377 continue;
378 }
379
380 /* otherwise, if it's a chrdev, check the bits */
381 if (s.st_rdev != DEV_TO_NUM(fs)) {
382 /* delete it if they don't match */
383 if (unlink(path) < 0) {
384 fprintf(stderr, "Unable to unlink existing device file %s\n", path);
385 goto remove;
386 }
387 }
388
389 /* the existing inode is correct. */
390 else {
391 /* if it's marked unmade then do the open to set it 'made' */
392 if (fs->unmade)
393 goto do_open;
394
395 /* otherwise just mark it as OK */
396 goto mark;
397 }
398
399 create:
400 /* OK, now do the mknod! */
401 if (mknod(path, S_IFCHR | 0777, DEV_TO_NUM(fs)) < 0) {
402 fprintf(stderr, " *** Unable to mknod device %d.%d to %s\n",
403 fs->major, fs->minor, path);
404 goto remove;
405 }
406
407 if (verbose)
408 fprintf(stderr, " *** mknod'd device %d,%d (%x) dev %s\n",
409 fs->major, fs->minor, (uint32_t)DEV_TO_NUM(fs), path);
410
411 do_open:
412 /* open it to activate it! */
413 fd = open(path, O_RDONLY);
414 if (fd >= 0) {
415 fprintf(stderr, " *** What?? our open succeeded??\n");
416 close(fd);
417 }
418
419 else {
420 switch (errno) {
421
422 case ENOENT:
423 /* this happens if the device has already been deleted in the kernel */
424 /* if ptr is valid, we just don't mark it, it will be removed later */
425 if (ptr == NULL)
426 unlink(path);
427 goto remove;
428
429 case EXDEV:
430 /* this means we succeeded */
431 if (verbose)
432 fprintf(stderr, " *** Open should have 'made' dev %s\n", path);
433 break;
434
435 default:
436 fprintf(stderr, " *** Oops.. unexpected error opening dev %s: %m\n", path);
437 }
438 }
439
440 mark:
441 if (ptr) {
442 /* if key is already there, update the state */
443 ptr->major = fs->major;
444 ptr->minor = fs->minor;
445 ptr->mark = 1;
446 }
447
448 else {
449 struct dev key = {
450 name: strdup(fs->name),
451 major: fs->major,
452 minor: fs->minor,
453 mark: 1
454 };
455
456 dev_insert(&key);
457 }
458 continue;
459
460 remove:
461 if (ptr) dev_remove(ptr);
462 }
463
464 /* finalize deletions */
465 dev_compress();
466
467 return STATUS_OK;
468 }
469
470
471 void start_fusdd(int set_verbose)
472 {
473 int fd;
474 int status;
475 verbose = set_verbose;
476
477 fprintf(stderr, "fusdd: FUSDd Starting...%s\n", verbose ? " (Verbose Mode)" : "");
478 umask(0);
479
480 /* check UID */
481 if (geteuid()) {
482 fprintf(stderr, "fusdd: Sorry, I must run as root!\n");
483 exit(1);
484 }
485
486 /* modprobe kfusd */
487 status = system("/sbin/modprobe kfusd");
488 if (status < 0) {
489 fprintf(stderr, "fusdd: modprobe failed: %m\n");
490 }
491 else if (status) {
492 fprintf(stderr, "fusdd: modprobe returned '%d', can't find fusd? Continuing...\n",
493 status);
494 }
495
496 /* mknod the FUSD control channel etc */
497 f_mkdir_with_parents(FUSD_CONTROL_DEVNAME);
498 if (mknod(FUSD_CONTROL_DEVNAME, S_IFCHR | 0777,
499 makedev(FUSD_DEV_MAJOR, FUSD_CONTROL_MINOR)) < 0)
500 if (errno != EEXIST)
501 fprintf(stderr, "Unable to mknod FUSD control device: %m\n");
502 if (mknod(FUSD_STATUS_DEVNAME, S_IFCHR | 0777,
503 makedev(FUSD_DEV_MAJOR, FUSD_STATUS_MINOR)) < 0)
504 if (errno != EEXIST)
505 fprintf(stderr, "Unable to mknod FUSD status device: %m\n");
506
507 /* open the FUSD status device */
508 fd = open(FUSD_STATUS_DEVNAME, O_RDONLY);
509 if (fd < 0) {
510 fprintf(stderr, "fusdd: Can't open %s.. Make sure FUSD module is inserted: %m\n",
511 FUSD_STATUS_DEVNAME);
512 goto delete;
513 }
514
515 /* turn on binary mode */
516 if (ioctl(fd, FUSD_STATUS_USE_BINARY, NULL) < 0) {
517 fprintf(stderr, "fusd: BINARY_MODE ioctl() failed: %m\n");
518 exit(1);
519 }
520
521 /* daemonize? */
522 if (!verbose) {
523 int status = fork();
524
525 if (status < 0) {
526 fprintf(stderr, "fusdd: Unable to fork!\n");
527 exit(1);
528 }
529
530 if (status)
531 return;
532
533 /* child: daemonize */
534 setpgrp();
535 chdir("/");
536 close(0);
537 close(1);
538 }
539
540 /* Tell FUSD who we are */
541 if (ioctl(fd, FUSD_STATUS_I_AM_FUSDD, NULL) < 0) {
542 if (errno == 524) {
543 fprintf(stderr, "fusdd: This FUSD uses DevFS: daemon exiting.\n");
544 return;
545 }
546 fprintf(stderr, "fusd: I_AM_FUSDD ioctl() failed: %m\n");
547 goto delete;
548 }
549
550 if (!verbose)
551 close(2);
552
553 {
554 fusd_status_context_t fsctx = {
555 fd: fd,
556 new_data_cb: process,
557 begin_full_cb: clear_marks,
558 end_full_cb: unlink_unmarked,
559 };
560
561 /* Now we read! */
562 while (1) {
563 int status;
564
565 /* process it */
566 status = fusd_status_process(&fsctx);
567
568 switch (status) {
569 case STATUS_ERR:
570 fprintf(stderr, "fusd: Read failed: %m\n");
571 exit(1);
572 case STATUS_DONE:
573 if (fusd_status_wait(&fsctx) < 0) {
574 fprintf(stderr, "fusd: Poll failed: %m\n");
575 exit(1);
576 }
577 }
578 }
579 }
580
581 delete:
582 unlink(FUSD_CONTROL_DEVNAME);
583 unlink(FUSD_STATUS_DEVNAME);
584 exit(1);
585 }
586
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.