Implemented optional 'statfs' for visible df(1) lufs filesystems entry.
[lufs.git] / lufsd / fsctl.c
1 /*
2  * fsctl.c
3  * Copyright (C) 2002 Florin Malita <mali@go.ro>
4  *
5  * This file is part of LUFS, a free userspace filesystem implementation.
6  * See http://lufs.sourceforge.net/ for updates.
7  *
8  * LUFS is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * LUFS is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <unistd.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <signal.h>
31 #include <dirent.h>
32 #include <fcntl.h>
33 #include <dlfcn.h>
34 #include <pthread.h>
35 #include <errno.h>
36 #ifndef __USE_GNU
37 #define __USE_GNU
38 #endif
39 #include <string.h>
40
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <sys/socket.h>
44 #include <sys/un.h>
45 #include <sys/wait.h>
46 #include <sys/ipc.h>
47 #include <sys/msg.h>
48
49 #include <lufs/proto.h>
50 #include <lufs/fs.h>
51
52 #include "list.h"
53 #include "message.h"
54 #include "fsctl.h"
55 #include "filesystem.h"
56 #include "dircache.h"
57
58 char sock_name[MAX_LEN];
59
60 static int usr1Handler_hit=0;
61 static void
62 usr1Handler(int sig){
63
64     TRACE("usr1Handler...");
65     usr1Handler_hit=1;
66 }
67
68 static void
69 sig_handler(int sig){
70     TRACE("got a signal: %d", sig);
71
72     signal(sig, sig_handler);
73 }
74
75 static void*
76 thread_launcher(void *params){
77     struct thread_info *p = (struct thread_info*)params;
78     struct file_system *fs = p->fs;
79     int sock = p->sock;
80     pid_t ppid = p->ppid;
81
82     TRACE("thread created");
83     
84     if(fs) {
85         handle_fs(fs, sock, ppid);
86     }else{
87         WARN("could not instantiate filesystem (out of mem?) !");
88     }
89
90     if(fs->fs_ops->umount){
91         TRACE("unmounting filesystem...");
92         fs->fs_ops->umount(fs->fs_context);
93     }
94     TRACE("freeing filesystem...");
95     fs->fs_ops->free(fs->fs_context);
96
97     free(fs);
98
99     close(sock);
100     TRACE("thread exiting...");
101
102     return NULL;
103 }
104
105 #define BUF_SIZE        1024
106 #define PASSWD          "/etc/passwd"
107 #define GROUP           "/etc/group"
108
109 static int
110 load_credentials(struct fs_ctl *ctl, struct file_system *fs){
111     static char buf[BUF_SIZE];
112     char srch_str[MAX_LEN + 4];
113     long int uid, gid;
114     int res, offset, chunk, readlen;
115     char *c;
116
117     TRACE("loading remote credentials for %s", ctl->cred.user);
118
119     if((!ctl->fs_ops->open) || (!ctl->fs_ops->read) || (!ctl->fs_ops->release)){
120         WARN("unsupported operation");
121         return -1;;
122     }
123
124     ctl->cred.uid = ctl->cred.gid = -1;
125
126     if(ctl->fs_ops->open(fs->fs_context, PASSWD, O_RDONLY) < 0){
127         TRACE("could not open %s", PASSWD);
128         return -1;
129     }
130
131     sprintf(srch_str, "\n%s:", ctl->cred.user);
132     chunk = strlen(srch_str) + 64;
133     readlen = BUF_SIZE - chunk - 1;
134
135     memset(buf, 32, chunk);
136     offset = 0;
137
138     do{
139         res = ctl->fs_ops->read(fs->fs_context, PASSWD, offset, readlen, (buf + chunk));
140         if(res > 0){
141             *(buf + chunk + res) = 0;
142
143             if((c = strstr(buf, srch_str))){
144                 TRACE("username found!");
145                 if(!(c = strchr(c + strlen(srch_str), ':'))){
146                     TRACE("separator not found!");
147                 }else{ 
148                     if(sscanf(c , ":%li:%li:", &uid, &gid) != 2){
149                         TRACE("uid/gid not found!");
150                     }else{
151                         TRACE("uid: %li, gid: %li", uid, gid);
152
153                         ctl->cred.uid = uid;
154                         ctl->cred.gid = gid;
155
156                         break;
157                     }
158                 }
159             }
160
161             memcpy(buf, buf + BUF_SIZE - chunk - 1, chunk);
162             offset += res;
163         }
164     }while(res == readlen);
165
166     ctl->fs_ops->release(fs->fs_context, PASSWD);
167
168     if(res <= 0){
169         TRACE("read failed");
170         return -1;
171     }
172
173     
174     if(ctl->fs_ops->open(fs->fs_context, GROUP, O_RDONLY) < 0){
175         TRACE("could not open %s", GROUP);
176         return -1;
177     }
178
179     sprintf(srch_str, ":%li:", (long)ctl->cred.gid);
180     chunk = strlen(srch_str) + 64;
181     readlen = BUF_SIZE - chunk - 1;
182
183     memset(buf, 32, chunk);
184     offset = 0;
185
186     do{
187         res = ctl->fs_ops->read(fs->fs_context, GROUP, offset, readlen, (buf + chunk));
188         if(res > 0){
189             *(buf + chunk + res) = 0;
190
191             if((c = strstr(buf, srch_str))){
192                 TRACE("group found!");
193                 if(!(c = (char*)memrchr(buf, '\n', (c - buf)))){
194                     TRACE("separator not found!");
195                 }else{ 
196                     *(strchr(c, ':')) = 0;
197                     if(strlen(c + 1) >= MAX_LEN){
198                         TRACE("groupname too long");
199                     }else{
200                         strcpy(ctl->cred.group, c + 1);
201                         TRACE("group: %s", ctl->cred.group);
202                         break;
203                     }
204                 }
205             }
206
207             memcpy(buf, buf + BUF_SIZE - chunk - 1, chunk);
208             offset += res;
209         }
210     }while(res == readlen);
211
212     ctl->fs_ops->release(fs->fs_context, GROUP);
213
214     if(res <= 0){
215         TRACE("read failed");
216         return -1;
217     }
218
219     return 0;
220 }
221
222
223 static struct fs_operations*
224 get_filesystem(struct fs_ctl *ctl, char *fs){
225     struct fs_operations *fops;
226     char *buf;
227     void *dlhandle;
228
229     if(!(buf = (char*)malloc(strlen(fs) + 32)))
230         return NULL;
231
232     sprintf(buf, "liblufs-%s.so", fs);
233     TRACE("trying to load %s", buf);
234
235     if(!(dlhandle = dlopen(buf, RTLD_LAZY))){
236         ERROR(dlerror());
237         goto fail;
238     }
239
240     TRACE("lib opened");
241         
242     if(!(fops = (struct fs_operations*)malloc(sizeof(struct fs_operations))))
243         goto fail_dl;
244
245     memset(fops, 0, sizeof(struct fs_operations));
246
247     sprintf(buf, "%s_init", fs);
248     if(!(fops->init = (void*(*)(struct list_head*, struct dir_cache*, struct credentials*, void**))dlsym(dlhandle, buf))){
249         ERROR(dlerror());
250         goto fail_fops;
251     }
252
253     sprintf(buf, "%s_free", fs);
254     if(!(fops->free = (void(*)(void*))dlsym(dlhandle, buf))){
255         ERROR(dlerror());
256         goto fail_fops;
257     }
258
259     sprintf(buf, "%s_mount", fs);
260     if(!(fops->mount = (int(*)(void*))dlsym(dlhandle, buf))){
261         ERROR(dlerror());
262         goto fail_fops;
263     }
264
265     sprintf(buf, "%s_umount", fs);
266     if(!(fops->umount = (void(*)(void*))dlsym(dlhandle, buf)))
267         ERROR(dlerror());
268
269     sprintf(buf, "%s_readdir", fs);
270     if(!(fops->readdir = (int(*)(void*, char*, struct directory*))dlsym(dlhandle, buf)))
271         ERROR(dlerror());
272
273     sprintf(buf, "%s_stat", fs);
274     if(!(fops->stat = (int(*)(void*, char*, struct lufs_fattr*))dlsym(dlhandle, buf)))
275         ERROR(dlerror());
276
277     sprintf(buf, "%s_mkdir", fs);
278     if(!(fops->mkdir = (int(*)(void*, char*, int))dlsym(dlhandle, buf)))
279         ERROR(dlerror());
280
281     sprintf(buf, "%s_rmdir", fs);
282     if(!(fops->rmdir = (int(*)(void*, char*))dlsym(dlhandle, buf)))
283         ERROR(dlerror());
284
285     sprintf(buf, "%s_create", fs);
286     if(!(fops->create = (int(*)(void*, char*, int))dlsym(dlhandle, buf)))
287         ERROR(dlerror());
288
289     sprintf(buf, "%s_unlink", fs);
290     if(!(fops->unlink = (int(*)(void*, char*))dlsym(dlhandle, buf)))
291         ERROR(dlerror());
292
293     sprintf(buf, "%s_rename", fs);
294     if(!(fops->rename = (int(*)(void*, char*, char*))dlsym(dlhandle, buf)))
295         ERROR(dlerror());
296
297     sprintf(buf, "%s_open", fs);
298     if(!(fops->open = (int(*)(void*, char*, unsigned))dlsym(dlhandle, buf)))
299         ERROR(dlerror());
300
301     sprintf(buf, "%s_release", fs);
302     if(!(fops->release = (int(*)(void*, char*))dlsym(dlhandle, buf)))
303         ERROR(dlerror());
304
305     sprintf(buf, "%s_read", fs);
306     if(!(fops->read = (int(*)(void*, char*, long long, unsigned long, char*))dlsym(dlhandle, buf)))
307         ERROR(dlerror());
308
309     sprintf(buf, "%s_write", fs);
310     if(!(fops->write = (int(*)(void*, char*, long long, unsigned long, char*))dlsym(dlhandle, buf)))
311         ERROR(dlerror());
312
313     sprintf(buf, "%s_readlink", fs);
314     if(!(fops->readlink = (int(*)(void*, char*, char*, int))dlsym(dlhandle, buf)))
315         ERROR(dlerror());
316
317     sprintf(buf, "%s_link", fs);
318     if(!(fops->link = (int(*)(void*, char*, char*))dlsym(dlhandle, buf)))
319         ERROR(dlerror());
320
321     sprintf(buf, "%s_symlink", fs);
322     if(!(fops->symlink = (int(*)(void*, char*, char*))dlsym(dlhandle, buf)))
323         ERROR(dlerror());
324
325     sprintf(buf, "%s_setattr", fs);
326     if(!(fops->setattr = (int(*)(void*, char*, struct lufs_fattr*))dlsym(dlhandle, buf)))
327         ERROR(dlerror());
328
329     sprintf(buf, "%s_statfs", fs);
330     /* 'statfs' is optional as it is new for lufs-0.9.6-captive. */
331     fops->statfs = (int(*)(void*, struct lufs_sbattr*))dlsym(dlhandle, buf);
332
333     TRACE("file system loaded");
334
335     ctl->dlhandle = dlhandle;
336     free(buf);
337     return fops;
338
339   fail_fops:
340     free(fops);
341   fail_dl:
342     dlclose(dlhandle);
343     dlhandle = NULL;
344   fail:  
345     free(buf);
346     return NULL;
347 }
348
349 static struct file_system*
350 new_fsinstance(struct fs_ctl *ctl){
351     struct file_system *fs;
352     
353     if(!(fs = (struct file_system*)malloc(sizeof(struct file_system))))
354         return NULL;
355
356     memset(fs, 0, sizeof(struct file_system));
357
358     fs->fs_ops = ctl->fs_ops;
359     fs->fs_credentials = &ctl->cred;
360     fs->fs_cache = ctl->cache;
361     fs->fs_config = ctl->cfg;
362
363     if(!(fs->fs_context = ctl->fs_ops->init(ctl->cfg, ctl->cache, fs->fs_credentials, &ctl->global_ctx))){
364         ERROR("could not initialize file system!");
365         free(fs);
366         return NULL;
367     }
368
369     return fs;
370 }
371
372 int
373 lu_fsctl_mount(struct fs_ctl *ctl){
374     int res;
375     struct file_system *fs;
376
377     if(!ctl->fs_ops)
378         return 0;
379
380     if(!(fs = new_fsinstance(ctl)))
381         return 0;
382         
383     res = ctl->fs_ops->mount(fs->fs_context);
384
385     if(res){
386         ctl->fs_available = fs;
387         fs->fs_mounted = 1;
388         if(load_credentials(ctl, fs) < 0)
389             TRACE("could not load credentials.");
390         else
391             TRACE("credentials loaded.");
392
393     }else{
394         WARN("fs mount failed...");
395         free(fs);
396     }
397
398     return res;
399 }
400
401
402 void
403 lu_fsctl_run(struct fs_ctl *ctl, int ssock, char *sn){
404     socklen_t len;
405     struct sockaddr_un addr;
406     int sock;
407     struct thread_info *info, *info_list=NULL;
408     struct sigaction sigaction_struct;
409
410     if(strlen(sn) >= MAX_LEN){
411         WARN("socket name too long!");
412         return;
413     }
414     
415     strcpy(sock_name, sn);
416
417 #if 0   /* signal(2) will not abort accept(2) below. */
418     signal(SIGUSR1, usr1Handler);
419 #else
420     memset(&sigaction_struct, 0, sizeof(sigaction_struct));
421     sigaction_struct.sa_handler = usr1Handler;
422     sigaction_struct.sa_flags = 0;      /* !SA_RESTART */
423     sigaction(SIGUSR1, &sigaction_struct, NULL);
424 #endif
425
426     signal(SIGPIPE, sig_handler);
427     signal(SIGTERM, sig_handler);
428     signal(SIGINT, sig_handler);
429     
430     while(!usr1Handler_hit){
431         len = sizeof(struct sockaddr_un);
432
433         if((sock = accept(ssock, (struct sockaddr*)&addr, &len)) < 0){
434             if(errno != EINTR){
435                 WARN("accept failed: %d(%s)", errno, strerror(errno));
436             }
437         }else{
438             TRACE("a client process connected.");
439
440             if((info = (struct thread_info*)malloc(sizeof(struct thread_info)))){
441
442                 if(ctl->fs_available){
443                     TRACE("using already mounted filesystem...");
444                     info->fs = ctl->fs_available;
445                     ctl->fs_available = NULL;
446                 }else
447                     info->fs = new_fsinstance(ctl);
448
449                 info->sock = sock;
450                 info->ppid = getpid();
451                 if(!pthread_create(&info->th_id, NULL, &thread_launcher, (void*)info)){
452                     info->next = info_list;
453                     info_list= info;
454                 }else{
455                     WARN("could not create thread!");
456                     free(info);
457                 }
458             }else{
459                 WARN("out of memory?!");
460             }
461         }
462     }
463
464     TRACE("joining threads...");
465     while ((info = info_list)){
466         info_list = info->next;
467         if (pthread_join(info->th_id, NULL))
468             WARN("could not join thread!");
469         free(info);
470     }
471     if (ctl->fs_available){
472         struct file_system *fs = ctl->fs_available;
473
474         if(fs->fs_ops->umount){
475             TRACE("unmounting filesystem...");
476             fs->fs_ops->umount(fs->fs_context);
477         }
478         TRACE("freeing filesystem...");
479         fs->fs_ops->free(fs->fs_context);
480
481         free(fs);
482     }
483     TRACE("socket name: %s", sock_name);
484     unlink(sock_name);
485 }
486
487 struct fs_ctl*
488 lu_fsctl_create(struct list_head *conf){
489     struct fs_ctl *ctl;
490     const char *fs_name, *user_name;
491
492     TRACE("creating fs_ctl");
493     
494     if(!(ctl = (struct fs_ctl*)malloc(sizeof(struct fs_ctl))))
495         return NULL;
496
497     memset(ctl, 0, sizeof(struct fs_ctl));
498
499     ctl->cache = lu_cache_create(conf);
500     ctl->cfg = conf;
501
502     if(!(fs_name = lu_opt_getchar(conf, "MOUNT", "fs"))){
503         ERROR("you need to specify a file system!");
504         free(ctl);
505         return NULL;
506     }
507
508      if(!(ctl->fs_ops = get_filesystem(ctl, (char*)fs_name))){ 
509         ERROR("unsupported file system: %s", fs_name); 
510         free(ctl); 
511         return NULL; 
512      }
513
514     if((user_name = lu_opt_getchar(conf, "MOUNT", "username")))
515         strcpy(ctl->cred.user, user_name);
516
517     return ctl;
518 }
519
520 void
521 lu_fsctl_destroy(struct fs_ctl *ctl){
522     TRACE("destroying fs_ctl");
523
524     lu_cache_destroy(ctl->cache);
525
526     if(ctl->dlhandle)
527         dlclose(ctl->dlhandle);
528
529     if(ctl->fs_available)
530         free(ctl->fs_available);
531
532     free(ctl);
533 }
534
535
536
537