Exit parent fsctl thread only after joining all of its child threads.
[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     TRACE("file system loaded");
330
331     ctl->dlhandle = dlhandle;
332     free(buf);
333     return fops;
334
335   fail_fops:
336     free(fops);
337   fail_dl:
338     dlclose(dlhandle);
339     dlhandle = NULL;
340   fail:  
341     free(buf);
342     return NULL;
343 }
344
345 static struct file_system*
346 new_fsinstance(struct fs_ctl *ctl){
347     struct file_system *fs;
348     
349     if(!(fs = (struct file_system*)malloc(sizeof(struct file_system))))
350         return NULL;
351
352     memset(fs, 0, sizeof(struct file_system));
353
354     fs->fs_ops = ctl->fs_ops;
355     fs->fs_credentials = &ctl->cred;
356     fs->fs_cache = ctl->cache;
357     fs->fs_config = ctl->cfg;
358
359     if(!(fs->fs_context = ctl->fs_ops->init(ctl->cfg, ctl->cache, fs->fs_credentials, &ctl->global_ctx))){
360         ERROR("could not initialize file system!");
361         free(fs);
362         return NULL;
363     }
364
365     return fs;
366 }
367
368 int
369 lu_fsctl_mount(struct fs_ctl *ctl){
370     int res;
371     struct file_system *fs;
372
373     if(!ctl->fs_ops)
374         return 0;
375
376     if(!(fs = new_fsinstance(ctl)))
377         return 0;
378         
379     res = ctl->fs_ops->mount(fs->fs_context);
380
381     if(res){
382         ctl->fs_available = fs;
383         fs->fs_mounted = 1;
384         if(load_credentials(ctl, fs) < 0)
385             TRACE("could not load credentials.");
386         else
387             TRACE("credentials loaded.");
388
389     }else{
390         WARN("fs mount failed...");
391         free(fs);
392     }
393
394     return res;
395 }
396
397
398 void
399 lu_fsctl_run(struct fs_ctl *ctl, int ssock, char *sn){
400     socklen_t len;
401     struct sockaddr_un addr;
402     int sock;
403     struct thread_info *info, *info_list=NULL;
404     struct sigaction sigaction_struct;
405
406     if(strlen(sn) >= MAX_LEN){
407         WARN("socket name too long!");
408         return;
409     }
410     
411     strcpy(sock_name, sn);
412
413 #if 0   /* signal(2) will not abort accept(2) below. */
414     signal(SIGUSR1, usr1Handler);
415 #else
416     memset(&sigaction_struct, 0, sizeof(sigaction_struct));
417     sigaction_struct.sa_handler = usr1Handler;
418     sigaction_struct.sa_flags = 0;      /* !SA_RESTART */
419     sigaction(SIGUSR1, &sigaction_struct, NULL);
420 #endif
421
422     signal(SIGPIPE, sig_handler);
423     signal(SIGTERM, sig_handler);
424     signal(SIGINT, sig_handler);
425     
426     while(!usr1Handler_hit){
427         len = sizeof(struct sockaddr_un);
428
429         if((sock = accept(ssock, (struct sockaddr*)&addr, &len)) < 0){
430             if(errno != EINTR){
431                 WARN("accept failed: %d(%s)", errno, strerror(errno));
432             }
433         }else{
434             TRACE("a client process connected.");
435
436             if((info = (struct thread_info*)malloc(sizeof(struct thread_info)))){
437
438                 if(ctl->fs_available){
439                     TRACE("using already mounted filesystem...");
440                     info->fs = ctl->fs_available;
441                     ctl->fs_available = NULL;
442                 }else
443                     info->fs = new_fsinstance(ctl);
444
445                 info->sock = sock;
446                 info->ppid = getpid();
447                 if(!pthread_create(&info->th_id, NULL, &thread_launcher, (void*)info)){
448                     info->next = info_list;
449                     info_list= info;
450                 }else{
451                     WARN("could not create thread!");
452                     free(info);
453                 }
454             }else{
455                 WARN("out of memory?!");
456             }
457         }
458     }
459
460     TRACE("joining threads...");
461     while ((info = info_list)){
462         info_list = info->next;
463         if (pthread_join(info->th_id, NULL))
464             WARN("could not join thread!");
465         free(info);
466     }
467     if (ctl->fs_available){
468         struct file_system *fs = ctl->fs_available;
469
470         if(fs->fs_ops->umount){
471             TRACE("unmounting filesystem...");
472             fs->fs_ops->umount(fs->fs_context);
473         }
474         TRACE("freeing filesystem...");
475         fs->fs_ops->free(fs->fs_context);
476
477         free(fs);
478     }
479     TRACE("socket name: %s", sock_name);
480     unlink(sock_name);
481 }
482
483 struct fs_ctl*
484 lu_fsctl_create(struct list_head *conf){
485     struct fs_ctl *ctl;
486     const char *fs_name, *user_name;
487
488     TRACE("creating fs_ctl");
489     
490     if(!(ctl = (struct fs_ctl*)malloc(sizeof(struct fs_ctl))))
491         return NULL;
492
493     memset(ctl, 0, sizeof(struct fs_ctl));
494
495     ctl->cache = lu_cache_create(conf);
496     ctl->cfg = conf;
497
498     if(!(fs_name = lu_opt_getchar(conf, "MOUNT", "fs"))){
499         ERROR("you need to specify a file system!");
500         free(ctl);
501         return NULL;
502     }
503
504      if(!(ctl->fs_ops = get_filesystem(ctl, (char*)fs_name))){ 
505         ERROR("unsupported file system: %s", fs_name); 
506         free(ctl); 
507         return NULL; 
508      }
509
510     if((user_name = lu_opt_getchar(conf, "MOUNT", "username")))
511         strcpy(ctl->cred.user, user_name);
512
513     return ctl;
514 }
515
516 void
517 lu_fsctl_destroy(struct fs_ctl *ctl){
518     TRACE("destroying fs_ctl");
519
520     lu_cache_destroy(ctl->cache);
521
522     if(ctl->dlhandle)
523         dlclose(ctl->dlhandle);
524
525     if(ctl->fs_available)
526         free(ctl->fs_available);
527
528     free(ctl);
529 }
530
531
532
533