http://prdownloads.sourceforge.net/lufs/lufs-0.9.7.tar.gz?download
[lufs.git] / kernel / Linux / 2.6 / proc.c
1 /*
2  * proc.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 #include <linux/version.h>
24 #include <linux/kernel.h>
25 #include <linux/module.h>
26 #include <linux/fs.h>
27 #include <linux/slab.h>
28 #include <linux/socket.h>
29 #include <linux/un.h>
30 #include <linux/types.h>
31 #include <linux/list.h>
32 #include <linux/smp_lock.h>
33 #include <linux/net.h>
34 #include <linux/vfs.h>
35 #include <linux/mount.h>
36
37 #include <asm/system.h>
38 #include <asm/uaccess.h>
39
40 #include "lufs.h"
41 #include "proc.h"
42
43 static int sock_send(struct socket *sock, struct iovec *iov, int len)
44 {
45     struct msghdr msg = {
46         .msg_name       = NULL,
47         .msg_namelen    = 0,
48         .msg_iov        = iov,
49         .msg_iovlen     = len,
50         .msg_control    = NULL,
51         .msg_controllen = 0,
52         .msg_flags      = 0
53     };
54     int res, i, size;
55     mm_segment_t fs;
56
57     for(i = 0, size = 0; i < len; i++)
58         size += iov[i].iov_len;
59     
60     fs = get_fs();
61     set_fs(get_ds());
62     res = sock_sendmsg(sock, &msg, size);
63     set_fs(fs);
64
65     return res;
66 }
67
68 static int sock_recv(struct socket *sock, struct iovec *iov, int len, int rsize, unsigned flags)
69 {
70     struct msghdr msg = {
71         .msg_flags      = flags,
72         .msg_name       = NULL,
73         .msg_namelen    = 0,
74         .msg_iov        = iov,
75         .msg_iovlen     = len,
76         .msg_control    = NULL,
77         .msg_controllen = 0
78     };
79     mm_segment_t fs;
80     int res, i, size;
81
82     for(i = 0, size = 0; i < len; i++)
83         size += iov[i].iov_len;
84
85     if(size < rsize){
86         VERBOSE("Trying to overflow old me?! Truncating...\n");
87         rsize = size;
88     }
89
90     fs = get_fs();
91     set_fs(get_ds());
92     res = sock_recvmsg(sock, &msg, rsize, flags);
93     set_fs(fs);
94
95     return res;
96 }
97
98 static int sock_connect(char *path, struct socket **s)
99 {
100     struct sockaddr_un addr;
101     int res;
102
103     if(strlen(path) > UNIX_PATH_MAX - 1){
104         WARN("unix domain path too long: %s", path);
105         return -1;
106     }
107
108     addr.sun_family = AF_UNIX;
109     strcpy(addr.sun_path, path);
110
111     if((res = sock_create(PF_UNIX, SOCK_STREAM, 0, s)) < 0){
112         WARN("failed to create a unix domain socket!\n");
113         return res;
114     }
115
116     if((res = (*s)->ops->connect(*s, (struct sockaddr*)&addr, sizeof(addr), 0)) < 0){
117         WARN("failed to connect the socket: %d!\n", res);
118         return res;
119     }
120     return 0;
121 }
122
123 static int slot_reconnect(struct lufs_sb_info *info, struct server_slot *slot)
124 {
125     int res = 0, tries = 0;
126
127     if(slot->s_sock){
128         TRACE("closing socket.\n");
129         sock_release(slot->s_sock);
130         slot->s_sock = NULL;
131     }
132
133     while(tries++ < LU_MAXTRIES && (res = sock_connect(info->server_socket, &slot->s_sock)) < 0){
134         TRACE("retrying...\n");
135         sock_release(slot->s_sock);
136         slot->s_sock = NULL;
137     }
138
139     if(res >= 0){
140         TRACE("successfully reconnected.\n");
141     }
142
143     return res;
144 }
145
146 void lu_empty_slots(struct lufs_sb_info *info)
147 {
148     struct server_slot *slot;
149
150     while(!list_empty(&info->slots)){
151         slot = list_entry(info->slots.next, struct server_slot, s_list);
152         if(slot->s_sock)
153             sock_release(slot->s_sock);
154         list_del(&slot->s_list);
155         if(slot->s_buf)
156             kfree(slot->s_buf);
157         kfree(slot);
158     }
159 }
160
161 static int do_execute(struct socket *sock, unsigned short cmd, unsigned short msglen, struct iovec *siov, unsigned short slen, struct iovec *riov, unsigned short rlen)
162 {
163     struct lu_msg msg;
164     struct iovec iov;
165     int res;
166
167     TRACE("msg_len: %d\n", msglen);
168     
169     msg.msg_version = PVERSION;
170     msg.msg_type = cmd;
171     msg.msg_datalen = msglen;
172     msg.msg_pid = current->pid;
173
174     iov.iov_base = &msg;
175     iov.iov_len = sizeof(struct lu_msg);
176
177     if((res = sock_send(sock, &iov, 1)) < 0){
178         WARN("sock_send failed!\n");
179         return res;
180     }
181     if((res = sock_send(sock, siov, slen)) < 0){
182         WARN("sock_send failed!\n");
183         return res;
184     }
185
186     iov.iov_base = &msg;
187     iov.iov_len = sizeof(struct lu_msg);
188     if((res = sock_recv(sock, &iov, 1, sizeof(struct lu_msg), 0)) < 0){
189         WARN("sock_recv failed!\n");
190         return res;
191     }
192     if(res != sizeof(struct lu_msg)){
193         WARN("Ayeeee, didn't read a whole header!\n");
194         return -EBUSY;
195     }
196     
197     if((msg.msg_datalen == 0))
198         return msg.msg_type;
199
200     if(riov == NULL){
201         WARN("Unexpected data!!! Getting out of sync...\n");
202         return -1;
203     }
204         
205     if((res = sock_recv(sock, riov, rlen, msg.msg_datalen, 0)) < 0){
206         WARN("sock_recv failed!\n");
207         return res;
208     }
209
210     return msg.msg_type;
211 }
212
213 struct server_slot* lu_getslot(struct lufs_sb_info *info)
214 {
215     struct list_head *p, *nd_best = NULL;
216     struct server_slot *slot;
217     int gotlock = 0;
218
219     /* Look for a slot used by this process before */
220     read_lock(&info->lock);
221     list_for_each(p, &info->slots)
222         if(list_entry(p, struct server_slot, s_list)->s_lastpid == current->pid){
223             TRACE("found a previous used slot for %u.\n", current->pid);
224             if(down_trylock(&list_entry(p, struct server_slot, s_list)->s_lock) == 0){
225                 gotlock = 1;
226                 break;
227             }
228             TRACE("oops! I still hold the lock! forget this one...\n");
229         }else 
230             if(!nd_best){
231                 nd_best = p;
232             }
233
234     /* if we couldn't find one, take the first not locked by us */      
235     if(p == &info->slots){
236         if(!nd_best){
237             ERROR("deadlock: all locks owned by us!\n");
238             read_unlock(&info->lock);
239             return NULL;
240         }else
241             p = nd_best;
242         
243     }
244     read_unlock(&info->lock);
245
246     slot = list_entry(p, struct server_slot, s_list);
247     
248     /* Get the lock on that slot */
249     if(!gotlock)
250         if(down_interruptible(&slot->s_lock))
251             return NULL;
252
253     slot->s_lastpid = current->pid;
254
255     /* Move it to the tail */
256     write_lock(&info->lock);
257     list_del(p);
258     list_add_tail(p, &info->slots);
259     write_unlock(&info->lock);
260
261     return slot;
262 }
263
264 void lu_putslot(struct server_slot *slot)
265 {
266     up(&slot->s_lock);
267 }
268
269 int lu_execute(struct lufs_sb_info *info, struct server_slot *slot, unsigned short cmd, struct iovec *siov, unsigned short slen, struct iovec *riov, unsigned short rlen)
270 {
271     int res, i, msglen;
272     struct iovec bkup[LU_MAXIOVEC];
273
274     for(i = 0, msglen = 0; i < slen; i++){
275         bkup[i] = siov[i];
276         msglen += siov[i].iov_len;
277     }
278
279     if(slot->s_sock == NULL){
280         TRACE("slot not connected.\n");
281         if((res = slot_reconnect(info, slot)) < 0){
282             ERROR("failed to connect!\n");
283             goto out;
284         }
285     }
286
287     if((res = do_execute(slot->s_sock, cmd, msglen, siov, slen, riov, rlen)) < 0){
288         TRACE("do_execute failed!\n");
289
290         if(signal_pending(current) && (!sigismember(&current->pending.signal, SIGPIPE))){
291             TRACE("interrupted by a signal. disconnecting this slot...\n");
292             sock_release(slot->s_sock);
293             slot->s_sock = NULL;
294             goto out;
295         }
296         
297         if(sigismember(&current->pending.signal, SIGPIPE)){
298             TRACE("got a SIGPIPE\n");
299             sigdelset(&current->pending.signal, SIGPIPE);
300         }
301
302         if((res = slot_reconnect(info, slot)) < 0){
303             ERROR("could't reconnect!\n");
304             goto out;
305         }
306             
307         for(i = 0; i < slen; i++)
308             siov[i] = bkup[i];
309                 
310         if((res = do_execute(slot->s_sock, cmd, msglen, siov, slen, riov, rlen)) < 0){
311             ERROR("error executing command!\n");
312             goto out;
313         }
314     }
315     
316  out:
317     return res;
318 }
319
320 int lu_getname(struct dentry *d, char *name, int max)
321 {
322     int len = 0;
323     struct dentry *p;
324     struct lufs_sb_info *info = GET_INFO(d->d_sb);
325     
326     for(p = d; p != p->d_parent; p = p->d_parent)
327         len += p->d_name.len + 1;
328
329     TRACE("root: %s, rootlen: %d, namelen: %d\n", info->root, info->rootlen, len);
330     
331     if(len + info->rootlen > max)
332         return -1;
333
334     strcpy(name, info->root);
335
336     if(len + info->rootlen == 0){
337         strcat(name, "/");
338         goto out;
339     }
340     
341     len += info->rootlen;
342
343     name[len] = 0;
344     for(p = d; p != p->d_parent; p = p->d_parent){
345         len -= p->d_name.len;
346         strncpy(&(name[len]), p->d_name.name, p->d_name.len);
347         name[--len] = '/';
348     }
349
350 out:
351     TRACE("name resolved to %s\n", name);
352     return 0;
353 }
354
355 int lu_getname_dumb(struct dentry *d, char *name, int max)
356 {
357     int len = 0;
358     struct dentry *p;
359
360     for(p = d; p != p->d_parent; p = p->d_parent)
361         len += p->d_name.len + 1;
362
363     if(len > max)
364         return -1;
365
366     if(len == 0){
367         name[0] = '/';
368         name[1] = 0;
369         goto out;
370     }
371
372     name[len] = 0;
373     for(p = d; p != p->d_parent; p = p->d_parent){
374         len -= p->d_name.len;
375         strncpy(&(name[len]), p->d_name.name, p->d_name.len);
376         name[--len] = '/';
377     }
378
379 out:
380     return 0;
381 }
382
383 static void init_root_dirent(struct lufs_sb_info *server, struct lufs_fattr *fattr)
384 {
385     memset(fattr, 0, sizeof(struct lufs_fattr));
386     fattr->f_nlink = 1;
387     fattr->f_uid = server->config.uid;
388     fattr->f_gid = server->config.gid;
389     fattr->f_blksize = 512;
390     fattr->f_ino = 2;
391     fattr->f_mtime = CURRENT_TIME.tv_sec;
392     fattr->f_mode = S_IRUSR | S_IRGRP | S_IROTH | S_IXUSR | S_IXGRP | S_IXOTH | S_IFDIR | server->config.dmode;
393     fattr->f_size = 512;
394     fattr->f_blocks = 1;
395 }
396
397 void lu_lookup_root(struct lufs_sb_info *server, struct lufs_fattr *fattr)
398 {
399     struct server_slot *slot;
400     struct iovec siov, riov;
401     int res;
402
403     TRACE("in\n");
404
405     if((slot = lu_getslot(server)) == NULL){
406         init_root_dirent(server, fattr);
407         return;
408     }
409     
410     if(server->rootlen)
411         strcpy(slot->s_buf, server->root);
412     else
413         strcpy(slot->s_buf, "/");
414         
415     TRACE("stating root %s\n", slot->s_buf);
416
417     siov.iov_base = slot->s_buf;
418     siov.iov_len = strlen(slot->s_buf) + 1;
419     riov.iov_base = fattr;
420     riov.iov_len = sizeof(struct lufs_fattr);
421
422     if((res = lu_execute(server, slot, PTYPE_STAT, &siov, 1, &riov, 1)) < 0){
423         init_root_dirent(server, fattr);
424         goto out;
425     }
426
427     if(PIS_ERROR(res)){
428         WARN("stat failed!\n");
429         init_root_dirent(server, fattr);
430         goto out;
431     }
432
433     lu_fixattrs(server, fattr);
434
435     fattr->f_ino = 2;
436
437   out:
438     TRACE("out\n");
439     lu_putslot(slot);
440 }
441
442 void lu_fixattrs(struct lufs_sb_info *info, struct lufs_fattr *fattr)
443 {
444
445     fattr->f_blksize = LU_BLOCKSIZE;
446     
447     if(S_ISREG(fattr->f_mode) || S_ISDIR(fattr->f_mode))
448         fattr->f_blocks = (fattr->f_size + LU_BLOCKSIZE - 1) / LU_BLOCKSIZE;
449     else
450         fattr->f_blocks = 0;
451
452     if(info->config.own_fs){
453
454         if(!fattr->f_uid)
455             fattr->f_mode = (fattr->f_mode & ~S_IRWXU) | ((fattr->f_mode & S_IRWXO)*(S_IRWXU/S_IRWXO));
456
457         if(!fattr->f_gid)
458             fattr->f_mode = (fattr->f_mode & ~S_IRWXG) | ((fattr->f_mode & S_IRWXO)*(S_IRWXG/S_IRWXO));
459         
460         fattr->f_uid = info->config.uid;
461         fattr->f_gid = info->config.gid;
462
463     }else{
464         
465         if(fattr->f_uid)
466             fattr->f_uid = info->config.uid;
467         else
468             fattr->f_uid = LU_DEF_UID;
469
470         if(fattr->f_gid)
471             fattr->f_gid = info->config.gid;
472         else
473             fattr->f_gid = LU_DEF_GID;
474     }
475
476     if(fattr->f_mode & S_IFDIR)
477         fattr->f_mode |= info->config.dmode;
478     else
479         fattr->f_mode |= info->config.fmode;
480 }
481
482 void lu_xlate_symlink(char *link, char *target, char *buf)
483 {
484     int i;
485     char *c1, *c2 = link;
486
487     TRACE("translating %s->%s\n", link, target);
488
489     for(c1 = strchr(link, '/'); c1 && !strncmp(link, target, c1 - link); c2 = c1, c1 = strchr(c1 + 1, '/'));
490
491     TRACE("disjoint paths: %s, %s\n", c2, target + (c2 - link));
492
493     for(i = 0, c1 = c2; (c1 = strchr(c1 + 1, '/')); i++);
494
495     strcpy(buf, "./");
496     
497     for(; i > 0; i--)
498         strcat(buf, "../");
499
500     strcat(buf, target + (c2 - link) + 1);
501     
502     TRACE("absolute link resolved to %s\n", buf);
503    
504 }
505