* global.h: Move fcntl.h inclusion here. Define O_BINARY.
[midnight-commander.git] / vfs / sfs.c
blob82de2434c8b14c18a538b8d0a1a967de74f13909
1 /*
2 * Single File fileSystem
4 * Copyright 1998 Pavel Machek, distribute under GPL
6 * $Id$
8 * This defines whole class of filesystems which contain single file
9 * inside. It is somehow similar to extfs, except that extfs makes
10 * whole virtual trees and we do only single virtual files.
12 * If you want to gunzip something, you should open it with #ugz
13 * suffix, DON'T try to gunzip it yourself.
15 * Namespace: exports vfs_sfs_ops */
17 #include <config.h>
18 #include <errno.h>
19 #include <sys/types.h>
20 #include <unistd.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <fcntl.h>
25 #include "utilvfs.h"
27 #include "vfs.h"
28 #include "local.h"
30 struct cachedfile {
31 char *name, *cache;
32 uid_t uid;
33 struct cachedfile *next;
36 static struct cachedfile *head;
38 #define MAXFS 32
39 static int sfs_no = 0;
40 static char *sfs_prefix[ MAXFS ];
41 static char *sfs_command[ MAXFS ];
42 static int sfs_flags[ MAXFS ];
43 #define F_1 1
44 #define F_2 2
45 #define F_NOLOCALCOPY 4
46 #define F_FULLMATCH 8
48 static int uptodate (char *name, char *cache)
50 return 1;
53 static int vfmake (vfs *me, char *name, char *cache)
55 char *inpath, *op;
56 int w;
57 char pad [10240];
58 char *s, *t = pad;
59 int was_percent = 0;
61 vfs_split (name, &inpath, &op);
62 if ((w = (*me->which) (me, op)) == -1)
63 vfs_die ("This cannot happen... Hopefully.\n");
65 if ((sfs_flags[w] & F_1) || (!strcmp (name, "/"))) ; else return -1;
66 /* if ((sfs_flags[w] & F_2) || (!inpath) || (!*inpath)); else return -1; */
67 if (!(sfs_flags[w] & F_NOLOCALCOPY)) {
68 s = mc_getlocalcopy (name);
69 if (!s)
70 return -1;
71 name = name_quote (s, 0);
72 g_free (s);
73 } else
74 name = name_quote (name, 0);
75 #define COPY_CHAR if (t-pad>sizeof(pad)) { g_free (name); return -1; } else *t++ = *s;
76 #define COPY_STRING(a) if ((t-pad)+strlen(a)>sizeof(pad)) { g_free (name); return -1; } else { strcpy (t, a); t+= strlen(a); }
77 for (s = sfs_command[w]; *s; s++) {
78 if (was_percent) {
80 char *ptr = NULL;
81 was_percent = 0;
83 switch (*s) {
84 case '1': ptr = name; break;
85 case '2': ptr = op + strlen (sfs_prefix[w]); break;
86 case '3': ptr = cache; break;
87 case '%': COPY_CHAR; continue;
89 COPY_STRING (ptr);
90 } else {
91 if (*s == '%')
92 was_percent = 1;
93 else
94 COPY_CHAR;
97 g_free (name);
99 if (my_system (EXECUTE_AS_SHELL, "/bin/sh", pad)) {
100 return -1;
103 return 0; /* OK */
106 static char *
107 redirect (vfs *me, char *name)
109 struct cachedfile *cur = head;
110 uid_t uid = vfs_uid;
111 char *cache;
112 int handle;
114 while (cur){
115 if ((!strcmp (name, cur->name)) &&
116 (uid == cur->uid) &&
117 (uptodate (cur->name, cur->cache)))
118 /* FIXME: when not uptodate, we might want to kill cache
119 * file immediately, not to wait until timeout. */ {
120 vfs_stamp (&vfs_sfs_ops, cur);
121 return cur->cache;
123 cur = cur->next;
126 handle = mc_mkstemps (&cache, "sfs", NULL);
128 if (handle == -1) {
129 return "/SOMEONE_PLAYING_DIRTY_TMP_TRICKS_ON_US";
132 close (handle);
134 if (!vfmake (me, name, cache)){
135 cur = g_new (struct cachedfile, 1);
136 cur->name = g_strdup (name);
137 cur->cache = cache;
138 cur->uid = uid;
139 cur->next = head;
140 head = cur;
142 vfs_add_noncurrent_stamps (&vfs_sfs_ops, (vfsid) head, NULL);
143 vfs_rm_parents (NULL);
145 return cache;
148 unlink (cache);
149 g_free (cache);
150 return "/I_MUST_NOT_EXIST";
153 static void *
154 sfs_open (vfs *me, char *path, int flags, int mode)
156 int *sfs_info;
157 int fd;
159 path = redirect (me, path);
160 fd = open (path, NO_LINEAR(flags), mode);
161 if (fd == -1)
162 return 0;
164 sfs_info = g_new (int, 1);
165 *sfs_info = fd;
167 return sfs_info;
170 static int sfs_stat (vfs *me, char *path, struct stat *buf)
172 path = redirect (me, path);
173 return stat (path, buf);
176 static int sfs_lstat (vfs *me, char *path, struct stat *buf)
178 path = redirect (me, path);
179 #ifndef HAVE_STATLSTAT
180 return lstat (path, buf);
181 #else
182 return statlstat (path, buf);
183 #endif
186 static int sfs_chmod (vfs *me, char *path, int mode)
188 path = redirect (me, path);
189 return chmod (path, mode);
192 static int sfs_chown (vfs *me, char *path, int owner, int group)
194 path = redirect (me, path);
195 return chown (path, owner, group);
198 static int sfs_utime (vfs *me, char *path, struct utimbuf *times)
200 path = redirect (me, path);
201 return utime (path, times);
204 static int sfs_readlink (vfs *me, char *path, char *buf, int size)
206 path = redirect (me, path);
207 return readlink (path, buf, size);
210 static vfsid sfs_getid (vfs *me, char *path, struct vfs_stamping **parent)
211 { /* FIXME: what should I do? */
212 vfs *v;
213 vfsid id;
214 struct vfs_stamping *par;
215 struct cachedfile *cur = head;
217 while (cur) {
218 if ((!strcmp( path, cur->name )) &&
219 (vfs_uid == cur->uid))
220 break;
221 cur = cur->next;
224 *parent = NULL;
226 if (!cur)
227 return (vfsid)(-1);
230 char *path2 = g_strdup (path);
231 v = vfs_split (path2, NULL, NULL); /* Strip suffix which led to this being sfs */
232 v = vfs_split (path2, NULL, NULL); /* ... and learn whoever was the parent system */
233 id = (*v->getid) (v, path2, &par);
234 g_free (path2);
237 if (id != (vfsid)-1) {
238 *parent = g_new (struct vfs_stamping, 1);
239 (*parent)->v = v;
240 (*parent)->id = id;
241 (*parent)->parent = par;
242 (*parent)->next = NULL;
244 return (vfsid) cur;
247 static void sfs_free (vfsid id)
249 struct cachedfile *which = (struct cachedfile *) id;
250 struct cachedfile *cur, *prev;
252 for (cur = head, prev = 0; cur && cur != which; prev = cur, cur = cur->next)
254 if (!cur)
255 vfs_die( "Free of thing which is unknown to me\n" );
256 unlink (cur->cache);
258 if (prev)
259 prev->next = cur->next;
260 else
261 head = cur->next;
263 g_free (cur->cache);
264 g_free (cur->name);
265 g_free (cur);
268 static void sfs_fill_names (vfs *me, void (*func)(char *))
270 struct cachedfile *cur = head;
272 while (cur){
273 (*func)(cur->name);
274 cur = cur->next;
278 static int sfs_nothingisopen (vfsid id)
280 /* FIXME: Investigate whether have to guard this like in
281 the other VFSs (see fd_usage in extfs) -- Norbert */
282 return 1;
285 static char *sfs_getlocalcopy (vfs *me, char *path)
287 path = redirect (me, path);
288 return g_strdup (path);
291 static int sfs_ungetlocalcopy (vfs *me, char *path, char *local, int has_changed)
293 g_free(local);
294 return 0;
297 static int sfs_init (vfs *me)
299 char *mc_sfsini;
300 FILE *cfg;
302 mc_sfsini = concat_dir_and_file (mc_home, "extfs" PATH_SEP_STR "sfs.ini");
303 cfg = fopen (mc_sfsini, "r");
305 if (!cfg){
306 fprintf (stderr, _("Warning: file %s not found\n"), mc_sfsini);
307 g_free (mc_sfsini);
308 return 0;
310 g_free (mc_sfsini);
312 sfs_no = 0;
313 while (sfs_no < MAXFS){
314 char key[256];
315 char *c, *semi = NULL, flags = 0;
317 if (!fgets (key, sizeof (key), cfg))
318 break;
320 if (*key == '#')
321 continue;
323 for (c = key; *c; c++)
324 if ((*c == ':') || (*c == '/')){
325 semi = c;
326 if (*c == '/'){
327 *c = 0;
328 flags |= F_FULLMATCH;
330 break;
333 if (!semi){
334 fprintf (stderr, _("Warning: Invalid line in sfs.ini:\n%s\n"), key);
335 continue;
338 c = semi + 1;
339 while ((*c != ' ') && (*c != '\t')) {
340 switch (*c) {
341 case '1': flags |= F_1; break;
342 case '2': flags |= F_2; break;
343 case 'R': flags |= F_NOLOCALCOPY; break;
344 default:
345 fprintf (stderr, _("Warning: Invalid flag %c in sfs.ini:\n%s\n"), *c, key);
347 c++;
349 c++;
350 *(semi+1) = 0;
351 if ((semi = strchr (c, '\n')))
352 *semi = 0;
354 sfs_prefix [sfs_no] = g_strdup (key);
355 sfs_command [sfs_no] = g_strdup (c);
356 sfs_flags [sfs_no] = flags;
357 sfs_no++;
359 fclose (cfg);
360 return 1;
363 static void
364 sfs_done (vfs *me)
366 int i;
368 for (i = 0; i < sfs_no; i++){
369 g_free (sfs_prefix [i]);
370 g_free (sfs_command [i]);
371 sfs_prefix [i] = sfs_command [i] = NULL;
373 sfs_no = 0;
376 static int
377 sfs_which (vfs *me, char *path)
379 int i;
381 for (i = 0; i < sfs_no; i++)
382 if (sfs_flags [i] & F_FULLMATCH) {
383 if (!strcmp (path, sfs_prefix [i]))
384 return i;
385 } else
386 if (!strncmp (path, sfs_prefix [i], strlen (sfs_prefix [i])))
387 return i;
389 return -1;
392 vfs vfs_sfs_ops = {
393 NULL, /* This is place of next pointer */
394 "sfs",
395 F_EXEC, /* flags */
396 NULL, /* prefix */
397 NULL, /* data */
398 0, /* errno */
399 sfs_init,
400 sfs_done,
401 sfs_fill_names,
402 sfs_which,
404 sfs_open,
405 local_close,
406 local_read,
407 NULL,
409 NULL,
410 NULL,
411 NULL,
412 NULL,
413 NULL,
415 sfs_stat,
416 sfs_lstat,
417 local_fstat,
419 sfs_chmod,
420 sfs_chown,
421 sfs_utime,
423 sfs_readlink,
424 NULL,
425 NULL,
426 NULL,
428 NULL,
429 NULL,
430 local_errno,
431 local_lseek,
432 NULL,
434 sfs_getid,
435 sfs_nothingisopen,
436 sfs_free,
438 sfs_getlocalcopy,
439 sfs_ungetlocalcopy,
441 NULL,
442 NULL,
443 NULL,
444 NULL
446 #ifdef HAVE_MMAP
447 ,local_mmap,
448 local_munmap
449 #endif