c111c9da8848b127379f10e61d1a9bc07a802901
[silc.git] / lib / silcsftp / sftp_fs_memory.c
1 /*
2
3   sftp_fs_memory.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2001 - 2007 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program 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 */
19 /* $Id$ */
20 /* XXX TODO Win32 support */
21
22 #include "silc.h"
23 #include "silcsftp.h"
24 #include "silcsftp_fs.h"
25 #include "sftp_util.h"
26
27 #define DIR_SEPARATOR "/"
28
29 const struct SilcSFTPFilesystemOpsStruct silc_sftp_fs_memory;
30
31 typedef struct MemFSEntryStruct {
32   struct MemFSEntryStruct **entry;  /* Files and sub-directories */
33   SilcUInt32 entry_count;           /* Number of files and sub-directories */
34   struct MemFSEntryStruct *parent;  /* non-NULL if `directory' is TRUE,
35                                        includes parent directory. */
36   SilcUInt32 created;               /* Time of creation */
37   char *name;                       /* Name of the entry */
38   char *data;                       /* Data of the entry */
39   unsigned int directory : 1;       /* Set if this is directory */
40   unsigned int perm : 7;            /* Permissions */
41 } *MemFSEntry;
42
43 /* File handle. */
44 typedef struct {
45   SilcUInt32 handle;                /* Handle index */
46   int fd;                           /* Real file handle */
47   MemFSEntry entry;                 /* Filesystem entry */
48 } *MemFSFileHandle;
49
50 /* Memory filesystem */
51 typedef struct {
52   MemFSEntry root;                  /* Root of the filesystem hierarchy */
53   SilcSFTPFSMemoryPerm root_perm;
54   MemFSFileHandle *handles;         /* Open file handles */
55   SilcUInt32 handles_count;
56 } *MemFS;
57
58 /* Generates absolute path from relative path that may include '.' and '..'
59    in the path. */
60
61 static char *memfs_expand_path(MemFSEntry root, const char *path)
62 {
63   if (!strstr(path, "./") && !strstr(path, "../") &&
64       !strstr(path, "/..") && !strstr(path, "/."))
65     return strdup(path);
66
67   /* XXX TODO */
68   return NULL;
69 }
70
71 /* Add `entry' to directory `dir'. */
72
73 static SilcBool memfs_add_entry(MemFSEntry dir, MemFSEntry entry,
74                                 SilcBool check_perm)
75 {
76   int i;
77
78   /* Must be both write and exec permissions */
79   if (check_perm &&
80       !((dir->perm & SILC_SFTP_FS_PERM_WRITE) &&
81         (dir->perm & SILC_SFTP_FS_PERM_EXEC)))
82     return FALSE;
83
84   if (!dir->entry) {
85     dir->entry = silc_calloc(3, sizeof(*entry));
86     if (!dir->entry)
87       return FALSE;
88     dir->entry[0] = entry;
89     dir->entry_count = 3;
90     entry->created = time(0);
91     return TRUE;
92   }
93
94   for (i = 0; i < dir->entry_count; i++) {
95     if (dir->entry[i])
96       continue;
97
98     dir->entry[i] = entry;
99     entry->created = time(0);
100     return TRUE;
101   }
102
103   dir->entry = silc_realloc(dir->entry, sizeof(*dir->entry) *
104                             (dir->entry_count + 3));
105   if (!dir->entry)
106     return FALSE;
107   for (i = dir->entry_count + 1; i < dir->entry_count + 3; i++)
108     dir->entry[i] = NULL;
109   dir->entry[dir->entry_count] = entry;
110   dir->entry_count += 3;
111   entry->created = time(0);
112
113   return TRUE;
114 }
115
116 /* Removes entry `entry' and all entries under it recursively. */
117
118 static SilcBool memfs_del_entry(MemFSEntry entry, SilcBool check_perm)
119 {
120   int i;
121
122   /* Directories cannot be removed from remote access */
123   if (check_perm)
124     return FALSE;
125
126   silc_free(entry->name);
127   silc_free(entry->data);
128
129   /* Delete all entries recursively under this entry */
130   for (i = 0; i < entry->entry_count; i++) {
131     if (entry->entry[i]) {
132       if (!memfs_del_entry(entry->entry[i], FALSE))
133         return FALSE;
134     }
135   }
136   silc_free(entry->entry);
137
138   /* Remove from parent */
139   if (entry->parent) {
140     for (i = 0; i < entry->parent->entry_count; i++) {
141       if (entry->parent->entry[i] == entry) {
142         entry->parent->entry[i] = NULL;
143         break;
144       }
145     }
146   }
147
148   silc_free(entry);
149
150   return TRUE;
151 }
152
153 /* Finds first occurence of entry named `name' under the directory `dir'.
154    This does not check subdirectories recursively. */
155
156 static MemFSEntry memfs_find_entry(MemFSEntry dir, const char *name,
157                                    SilcUInt32 name_len)
158 {
159   int i;
160
161   for (i = 0; i < dir->entry_count; i++) {
162     if (!dir->entry[i])
163       continue;
164
165     if (!strncmp(name, dir->entry[i]->name, name_len))
166       return dir->entry[i];
167   }
168
169   return NULL;
170 }
171
172 /* Finds the entry by the `path' which may include full path or
173    relative path. */
174
175 static MemFSEntry memfs_find_entry_path(MemFSEntry dir, const char *p)
176 {
177   MemFSEntry entry = NULL;
178   int len;
179   char *path, *cp;
180
181   cp = path = memfs_expand_path(dir, p);
182
183   if (strlen(cp) == 1 && cp[0] == '/')
184     return dir;
185
186   if (cp[0] == '/')
187     cp++;
188   len = strcspn(cp, DIR_SEPARATOR);
189   while (cp && len) {
190     entry = memfs_find_entry(dir, cp, len);
191     if (!entry) {
192       silc_free(cp);
193       return NULL;
194     }
195     cp += len;
196     if (!strlen(cp))
197       break;
198     cp++;
199     len = strcspn(cp, DIR_SEPARATOR);
200     dir = entry;
201   }
202
203   silc_free(path);
204   return entry;
205 }
206
207 /* Deletes entry by the name `name' from the directory `dir'. This does
208    not check subdirectories recursively. */
209
210 static SilcBool memfs_del_entry_name(MemFSEntry dir, const char *name,
211                                      SilcUInt32 name_len, SilcBool check_perm)
212 {
213   MemFSEntry entry;
214
215   /* Files cannot be removed from remote access */
216   if (check_perm)
217     return FALSE;
218
219   entry = memfs_find_entry(dir, name, name_len);
220
221   if (entry)
222     return memfs_del_entry(entry, check_perm);
223
224   return FALSE;
225 }
226
227 /* Create new handle and add it to the list of open handles. */
228
229 static MemFSFileHandle memfs_create_handle(MemFS fs, int fd, MemFSEntry entry)
230 {
231   MemFSFileHandle handle;
232   int i;
233
234   handle = silc_calloc(1, sizeof(*handle));
235   if (!handle)
236     return NULL;
237   handle->fd = fd;
238   handle->entry = entry;
239
240   if (!fs->handles) {
241     fs->handles = silc_calloc(5, sizeof(*fs->handles));
242     if (!fs->handles)
243       return NULL;
244     fs->handles[0] = handle;
245     fs->handles_count = 5;
246
247     handle->handle = 0;
248
249     return handle;
250   }
251
252   for (i = 0; i < fs->handles_count; i++) {
253     if (fs->handles[i])
254       continue;
255
256     fs->handles[i] = handle;
257
258     handle->handle = i;
259
260     return handle;
261   }
262
263   fs->handles = silc_realloc(fs->handles, sizeof(*fs->handles) *
264                              (fs->handles_count + 5));
265   if (!fs->handles)
266     return NULL;
267   for (i = fs->handles_count + 1; i < fs->handles_count + 5; i++)
268     fs->handles[i] = NULL;
269   fs->handles[fs->handles_count] = handle;
270   handle->handle = fs->handles_count;
271   fs->handles_count += 5;
272
273   return handle;
274 }
275
276 /* Deletes the handle and remove it from the open handle list. */
277
278 static SilcBool memfs_del_handle(MemFS fs, MemFSFileHandle handle)
279 {
280   if (handle->handle > fs->handles_count)
281     return FALSE;
282
283   if (!fs->handles[handle->handle])
284     return FALSE;
285
286   if (fs->handles[handle->handle] == handle) {
287     fs->handles[handle->handle] = NULL;
288     if (handle->fd != -1)
289       silc_file_close(handle->fd);
290     silc_free(handle);
291     return TRUE;
292   }
293
294   return FALSE;
295 }
296
297 /* Find handle by handle index. */
298
299 static MemFSFileHandle memfs_find_handle(MemFS fs, SilcUInt32 handle)
300 {
301   if (handle > fs->handles_count)
302     return NULL;
303
304   if (!fs->handles[handle])
305     return NULL;
306
307   if (fs->handles[handle]->handle != handle)
308     return NULL;
309
310   return fs->handles[handle];
311 }
312
313 /* Allocates memory filesystem context and returns the context.  The
314    context can be given as argument to the silc_sftp_server_start function.
315    The context must be freed by the caller using the function
316    silc_sftp_fs_memory_free. The `perm' is the permissions for the root
317    directory of the filesystem (/ dir). */
318
319 SilcSFTPFilesystem silc_sftp_fs_memory_alloc(SilcSFTPFSMemoryPerm perm)
320 {
321   SilcSFTPFilesystem filesystem;
322   MemFS fs;
323
324   fs = silc_calloc(1, sizeof(*fs));
325   if (!fs)
326     return NULL;
327
328   fs->root = silc_calloc(1, sizeof(*fs->root));
329   if (!fs->root) {
330     silc_free(fs);
331     return NULL;
332   }
333
334   fs->root->perm = perm;
335   fs->root_perm = perm;
336   fs->root->directory = TRUE;
337   fs->root->name = strdup(DIR_SEPARATOR);
338   if (!fs->root->name) {
339     silc_free(fs->root);
340     silc_free(fs);
341   }
342
343   filesystem = silc_calloc(1, sizeof(*filesystem));
344   if (!filesystem) {
345     silc_free(fs->root->name);
346     silc_free(fs->root);
347     silc_free(fs);
348     return NULL;
349   }
350
351   filesystem->fs = (struct SilcSFTPFilesystemOpsStruct *)&silc_sftp_fs_memory;
352   filesystem->fs_context = (void *)fs;
353
354   return filesystem;
355 }
356
357 /* Frees the memory filesystem context. */
358
359 void silc_sftp_fs_memory_free(SilcSFTPFilesystem fs)
360 {
361   MemFS memfs = (MemFS)fs->fs_context;
362
363   silc_free(memfs->root);
364   silc_free(memfs);
365 }
366
367 /* Adds a new directory to the memory filesystem. Returns the directory
368    context that can be used to add for example files to the directory
369    or new subdirectories under the directory. The `dir' is the parent
370    directory of the directory to be added. If this directory is to be
371    added to the root directory the `dir' is NULL.  The `name' is the name
372    of the directory. If error occurs this returns NULL. The caller must
373    not free the returned context. The `perm' will indicate the permissions
374    for the directory and they work in POSIX style. */
375
376 void *silc_sftp_fs_memory_add_dir(SilcSFTPFilesystem fs, void *dir,
377                                   SilcSFTPFSMemoryPerm perm,
378                                   const char *name)
379 {
380   MemFS memfs = (MemFS)fs->fs_context;
381   MemFSEntry entry;
382
383   entry = silc_calloc(1, sizeof(*entry));
384   if (!entry)
385     return NULL;
386
387   entry->perm = perm;
388   entry->directory = TRUE;
389   entry->parent = dir ? dir : memfs->root;
390   entry->name = strdup(name);
391   if (!entry->name) {
392     silc_free(entry);
393     return NULL;
394   }
395
396   if (!memfs_add_entry(dir ? dir : memfs->root, entry, FALSE)) {
397     silc_free(entry->name);
398     silc_free(entry);
399     return NULL;
400   }
401
402   return entry;
403 }
404
405 /* Deletes a directory indicated by the `dir'. All files and subdirectories
406    in this directory is also removed.  If the `dir' is NULL then all
407    directories and files are removed from the filesystem. Returns TRUE
408    if the removing was success. This is the only way to remove directories
409    in memory file system. The filesystem does not allow removing directories
410    with remote access using the filesystem access function sftp_rmdir. */
411
412 SilcBool silc_sftp_fs_memory_del_dir(SilcSFTPFilesystem fs, void *dir)
413 {
414   MemFS memfs = (MemFS)fs->fs_context;
415   SilcBool ret;
416
417   if (dir)
418     return memfs_del_entry(dir, FALSE);
419
420   /* Remove from root */
421   ret = memfs_del_entry(memfs->root, FALSE);
422
423   memfs->root = silc_calloc(1, sizeof(*memfs->root));
424   if (!memfs->root)
425     return FALSE;
426
427   memfs->root->perm = memfs->root_perm;
428   memfs->root->directory = TRUE;
429   memfs->root->name = strdup(DIR_SEPARATOR);
430   if (!memfs->root->name) {
431     silc_free(memfs->root);
432     memfs->root = NULL;
433     return FALSE;
434   }
435
436   return ret;
437 }
438
439 /* Adds a new file to the directory indicated by the `dir'.  If the `dir'
440    is NULL the file is added to the root directory. The `filename' is the
441    filename in the directory. The `realpath' is the real filepath in the
442    physical filesystem. It is used to actually access the file from the
443    memory filesystem. The `perm' will indicate the permissions for th e
444    file and they work in POSIX style. Returns TRUE if the file was
445    added to the directory. */
446
447 SilcBool silc_sftp_fs_memory_add_file(SilcSFTPFilesystem fs, void *dir,
448                                       SilcSFTPFSMemoryPerm perm,
449                                       const char *filename,
450                                       const char *realpath)
451 {
452   MemFS memfs = (MemFS)fs->fs_context;
453   MemFSEntry entry;
454
455   entry = silc_calloc(1, sizeof(*entry));
456   if (!entry)
457     return FALSE;
458
459   entry->perm = perm;
460   entry->directory = FALSE;
461   entry->name = strdup(filename);
462   entry->data = strdup(realpath);
463   if (!entry->name || !entry->data) {
464     silc_free(entry->name);
465     silc_free(entry->data);
466     silc_free(entry);
467     return FALSE;
468   }
469
470   return memfs_add_entry(dir ? dir : memfs->root, entry, FALSE);
471 }
472
473 /* Removes a file indicated by the `filename' from the directory
474    indicated by the `dir'. Returns TRUE if the removing was success. */
475
476 SilcBool silc_sftp_fs_memory_del_file(SilcSFTPFilesystem fs, void *dir,
477                                       const char *filename)
478 {
479   MemFS memfs = (MemFS)fs->fs_context;
480
481   if (!filename)
482     return FALSE;
483
484   return memfs_del_entry_name(dir ? dir : memfs->root, filename,
485                             strlen(filename), FALSE);
486 }
487
488 SilcSFTPHandle memfs_get_handle(void *context, SilcSFTP sftp,
489                                 const unsigned char *data,
490                                 SilcUInt32 data_len)
491 {
492   MemFS fs = (MemFS)context;
493   SilcUInt32 handle;
494
495   if (data_len < 4)
496     return NULL;
497
498   SILC_GET32_MSB(handle, data);
499   return (SilcSFTPHandle)memfs_find_handle(fs, handle);
500 }
501
502 unsigned char *memfs_encode_handle(void *context, SilcSFTP sftp,
503                                    SilcSFTPHandle handle,
504                                    SilcUInt32 *handle_len)
505 {
506   unsigned char *data;
507   MemFSFileHandle h = (MemFSFileHandle)handle;
508
509   data = silc_calloc(4, sizeof(*data));
510   if (!data)
511     return NULL;
512
513   SILC_PUT32_MSB(h->handle, data);
514   *handle_len = 4;
515   return data;
516 }
517
518 void memfs_open(void *context, SilcSFTP sftp,
519                 const char *filename,
520                 SilcSFTPFileOperation pflags,
521                 SilcSFTPAttributes attrs,
522                 SilcSFTPHandleCallback callback,
523                 void *callback_context)
524 {
525   MemFS fs = (MemFS)context;
526   MemFSEntry entry;
527   MemFSFileHandle handle;
528   int flags = 0, fd;
529
530   /* CREAT and TRUNC not supported */
531   if ((pflags & SILC_SFTP_FXF_CREAT) || (pflags & SILC_SFTP_FXF_TRUNC)) {
532     (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, callback_context);
533     return;
534   }
535
536   /* Find such file */
537   entry = memfs_find_entry_path(fs->root, filename);
538   if (!entry) {
539     (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
540     return;
541   }
542
543   if (entry->directory || !entry->data) {
544     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
545     return;
546   }
547
548   /* Check for reading */
549   if ((pflags & SILC_SFTP_FXF_READ) &&
550       !(entry->perm & SILC_SFTP_FS_PERM_READ)) {
551     (*callback)(sftp, SILC_SFTP_STATUS_PERMISSION_DENIED, NULL,
552                 callback_context);
553     return;
554   }
555
556   /* Check for writing */
557   if (((pflags & SILC_SFTP_FXF_WRITE) || (pflags & SILC_SFTP_FXF_APPEND)) &&
558       !(entry->perm & SILC_SFTP_FS_PERM_WRITE)) {
559     (*callback)(sftp, SILC_SFTP_STATUS_PERMISSION_DENIED, NULL,
560                 callback_context);
561     return;
562   }
563
564   if ((pflags & SILC_SFTP_FXF_READ) && (pflags & SILC_SFTP_FXF_WRITE))
565     flags = O_RDWR;
566   else if (pflags & SILC_SFTP_FXF_READ)
567     flags = O_RDONLY;
568   else if (pflags & SILC_SFTP_FXF_WRITE)
569     flags = O_WRONLY;
570   if (pflags & SILC_SFTP_FXF_APPEND)
571     flags |= O_APPEND;
572
573   /* Attempt to open the file for real. */
574   fd = silc_file_open_mode(entry->data + 7, flags,
575                            (attrs->flags & SILC_SFTP_ATTR_PERMISSIONS ?
576                             attrs->permissions : 0600));
577   if (fd == -1) {
578     (*callback)(sftp, silc_sftp_map_errno(errno), NULL, callback_context);
579     return;
580   }
581
582   /* File opened, return handle */
583   handle = memfs_create_handle(fs, fd, entry);
584   if (handle)
585     (*callback)(sftp, SILC_SFTP_STATUS_OK, (SilcSFTPHandle)handle,
586                 callback_context);
587   else
588     (*callback)(sftp, SILC_SFTP_STATUS_PERMISSION_DENIED, NULL,
589                 callback_context);
590 }
591
592 void memfs_close(void *context, SilcSFTP sftp,
593                  SilcSFTPHandle handle,
594                  SilcSFTPStatusCallback callback,
595                  void *callback_context)
596 {
597   MemFS fs = (MemFS)context;
598   MemFSFileHandle h = (MemFSFileHandle)handle;
599   int ret;
600
601   if (h->fd != -1) {
602     ret = silc_file_close(h->fd);
603     if (ret == -1) {
604       (*callback)(sftp, silc_sftp_map_errno(errno), NULL, NULL,
605                   callback_context);
606       return;
607     }
608   }
609
610   memfs_del_handle(fs, h);
611   (*callback)(sftp, SILC_SFTP_STATUS_OK, NULL, NULL, callback_context);
612 }
613
614 void memfs_read(void *context, SilcSFTP sftp,
615                 SilcSFTPHandle handle,
616                 SilcUInt64 offset,
617                 SilcUInt32 len,
618                 SilcSFTPDataCallback callback,
619                 void *callback_context)
620 {
621   MemFSFileHandle h = (MemFSFileHandle)handle;
622   unsigned char data[63488];
623   int ret;
624
625   if (len > 63488)
626     len = 63488;
627
628   ret = lseek(h->fd, (off_t)offset, SEEK_SET);
629   if (ret < 0) {
630     if (!ret)
631       (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, 0, callback_context);
632     else
633       (*callback)(sftp, silc_sftp_map_errno(errno), NULL, 0, callback_context);
634     return;
635   }
636
637   /* Attempt to read */
638   ret = silc_file_read(h->fd, data, len);
639   if (ret <= 0) {
640     if (!ret)
641       (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, 0, callback_context);
642     else
643       (*callback)(sftp, silc_sftp_map_errno(errno), NULL, 0, callback_context);
644     return;
645   }
646
647   /* Return data */
648   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const unsigned char *)data,
649               ret, callback_context);
650 }
651
652 void memfs_write(void *context, SilcSFTP sftp,
653                  SilcSFTPHandle handle,
654                  SilcUInt64 offset,
655                  const unsigned char *data,
656                  SilcUInt32 data_len,
657                  SilcSFTPStatusCallback callback,
658                  void *callback_context)
659 {
660   MemFSFileHandle h = (MemFSFileHandle)handle;
661   int ret;
662
663   lseek(h->fd, (off_t)offset, SEEK_SET);
664
665   /* Attempt to write */
666   ret = silc_file_write(h->fd, data, data_len);
667   if (ret <= 0) {
668     (*callback)(sftp, silc_sftp_map_errno(errno), NULL, NULL,
669                 callback_context);
670     return;
671   }
672
673   (*callback)(sftp, SILC_SFTP_STATUS_OK, NULL, NULL, callback_context);
674 }
675
676 void memfs_remove(void *context, SilcSFTP sftp,
677                   const char *filename,
678                   SilcSFTPStatusCallback callback,
679                   void *callback_context)
680 {
681   /* Remove is not supported */
682   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL,
683               callback_context);
684 }
685
686 void memfs_rename(void *context, SilcSFTP sftp,
687                   const char *oldname,
688                   const char *newname,
689                   SilcSFTPStatusCallback callback,
690                   void *callback_context)
691 {
692   /* Rename is not supported */
693   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL,
694               callback_context);
695 }
696
697 void memfs_mkdir(void *context, SilcSFTP sftp,
698                  const char *path,
699                  SilcSFTPAttributes attrs,
700                  SilcSFTPStatusCallback callback,
701                  void *callback_context)
702 {
703   /* Mkdir is not supported */
704   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL,
705               callback_context);
706 }
707
708 void memfs_rmdir(void *context, SilcSFTP sftp,
709                  const char *path,
710                  SilcSFTPStatusCallback callback,
711                  void *callback_context)
712 {
713   /* Rmdir is not supported */
714   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL,
715               callback_context);
716 }
717
718 void memfs_opendir(void *context, SilcSFTP sftp,
719                    const char *path,
720                    SilcSFTPHandleCallback callback,
721                    void *callback_context)
722 {
723   MemFS fs = (MemFS)context;
724   MemFSEntry entry;
725   MemFSFileHandle handle;
726
727   if (!path || !strlen(path))
728     path = (const char *)DIR_SEPARATOR;
729
730   /* Find such directory */
731   entry = memfs_find_entry_path(fs->root, path);
732   if (!entry) {
733     (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
734     return;
735   }
736
737   if (!entry->directory) {
738     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
739     return;
740   }
741
742   /* Must be read permissions to open a directory */
743   if (!(entry->perm & SILC_SFTP_FS_PERM_READ)) {
744     (*callback)(sftp, SILC_SFTP_STATUS_PERMISSION_DENIED, NULL,
745                 callback_context);
746     return;
747   }
748
749   /* Directory opened, return handle */
750   handle = memfs_create_handle(fs, 0, entry);
751   if (handle)
752     (*callback)(sftp, SILC_SFTP_STATUS_OK, (SilcSFTPHandle)handle,
753                 callback_context);
754   else
755     (*callback)(sftp, SILC_SFTP_STATUS_PERMISSION_DENIED, NULL,
756                 callback_context);
757 }
758
759 void memfs_readdir(void *context, SilcSFTP sftp,
760                    SilcSFTPHandle handle,
761                    SilcSFTPNameCallback callback,
762                    void *callback_context)
763 {
764   MemFSFileHandle h = (MemFSFileHandle)handle;
765   MemFSEntry entry;
766   SilcSFTPName name;
767   SilcSFTPAttributes attrs;
768   int i;
769   char long_name[256];
770   SilcUInt64 filesize = 0;
771   char *date;
772   struct stat stats;
773
774   if (!h->entry->directory) {
775     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
776     return;
777   }
778
779   if (h->fd == -1) {
780     (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, callback_context);
781     return;
782   }
783
784   name = silc_calloc(1, sizeof(*name));
785   if (!name) {
786     (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, callback_context);
787     return;
788   }
789   for (i = h->fd; i < 100 + h->fd; i++) {
790     if (i >= h->entry->entry_count)
791       break;
792
793     entry = h->entry->entry[i];
794     if (!entry)
795       continue;
796
797     filesize = sizeof(*entry);
798     memset(long_name, 0, sizeof(long_name));
799
800     date = (char *)silc_time_string(entry->created);
801     if (strrchr(date, ':'))
802       *strrchr(date, ':') = '\0';
803
804     if (!entry->directory) {
805       filesize = silc_file_size(entry->data + 7);
806       memset(&stats, 0, sizeof(stats));
807       stat(entry->data + 7, &stats);
808     }
809
810     /* Long name format is:
811        drwx------   1   324210 Apr  8 08:40 mail/
812        1234567890 123 12345678 123456789012 */
813     silc_snprintf(long_name, sizeof(long_name) - 1,
814              "%c%c%c%c------ %3d %8llu %12s %s%s",
815              (entry->directory ? 'd' : '-'),
816              ((entry->perm & SILC_SFTP_FS_PERM_READ) ? 'r' : '-'),
817              ((entry->perm & SILC_SFTP_FS_PERM_WRITE) ? 'w' : '-'),
818              ((entry->perm & SILC_SFTP_FS_PERM_EXEC) ? 'x' : '-'),
819              (entry->directory ? (int)entry->entry_count : 1),
820 #ifndef SILC_WIN32
821              (unsigned long long)filesize,
822 #else
823              (unsigned long)filesize,
824 #endif
825              date, entry->name,
826              (entry->directory ? "/" :
827               ((entry->perm & SILC_SFTP_FS_PERM_EXEC) ? "*" : "")));
828
829     /* Add attributes */
830     attrs = silc_calloc(1, sizeof(*attrs));
831     if (!attrs) {
832       (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, callback_context);
833       return;
834     }
835     attrs->flags = (SILC_SFTP_ATTR_SIZE |
836                     SILC_SFTP_ATTR_UIDGID);
837     attrs->size = filesize;
838     attrs->uid = 0;                 /* We use always 0 UID and GID */
839     attrs->gid = 0;
840     if (!entry->directory) {
841       attrs->flags |= SILC_SFTP_ATTR_ACMODTIME;
842       attrs->atime = stats.st_atime;
843       attrs->mtime = stats.st_mtime;
844     }
845
846     /* Add the name */
847     silc_sftp_name_add(name, entry->name, long_name, attrs);
848   }
849
850   /* If we didn't read all then udpate the index for next read */
851   if (i >= h->entry->entry_count)
852     h->fd = -1;
853   else
854     h->fd = i;
855
856   /* If names was not found then return EOF. */
857   if (name->count == 0) {
858     (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, callback_context);
859     silc_sftp_name_free(name);
860     return;
861   }
862
863   /* Return name(s) */
864   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPName)name,
865               callback_context);
866
867   silc_sftp_name_free(name);
868 }
869
870 void memfs_stat(void *context, SilcSFTP sftp,
871                 const char *path,
872                 SilcSFTPAttrCallback callback,
873                 void *callback_context)
874 {
875   MemFS fs = (MemFS)context;
876   MemFSEntry entry;
877   SilcSFTPAttributes attrs;
878   int ret;
879   struct stat stats;
880
881   if (!path || !strlen(path))
882     path = (const char *)DIR_SEPARATOR;
883
884   /* Find such directory */
885   entry = memfs_find_entry_path(fs->root, path);
886   if (!entry) {
887     (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
888     return;
889   }
890
891   if (entry->directory || !entry->data) {
892     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
893     return;
894   }
895
896   /* Get real stat */
897   ret = stat(entry->data + 7, &stats);
898   if (ret == -1) {
899     (*callback)(sftp, silc_sftp_map_errno(errno), NULL, callback_context);
900     return;
901   }
902
903   attrs = silc_calloc(1, sizeof(*attrs));
904   if (!attrs) {
905     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
906     return;
907   }
908   attrs->flags = (SILC_SFTP_ATTR_SIZE |
909                   SILC_SFTP_ATTR_UIDGID |
910                   SILC_SFTP_ATTR_ACMODTIME);
911   attrs->size = stats.st_size;
912   attrs->uid = 0;                   /* We use always 0 UID and GID */
913   attrs->gid = 0;
914   attrs->atime = stats.st_atime;
915   attrs->mtime = stats.st_mtime;
916
917   /* Return attributes */
918   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPAttributes)attrs,
919               callback_context);
920
921   silc_sftp_attr_free(attrs);
922 }
923
924 void memfs_lstat(void *context, SilcSFTP sftp,
925                  const char *path,
926                  SilcSFTPAttrCallback callback,
927                  void *callback_context)
928 {
929   MemFS fs = (MemFS)context;
930   MemFSEntry entry;
931   SilcSFTPAttributes attrs;
932   int ret;
933   struct stat stats;
934
935   if (!path || !strlen(path))
936     path = (const char *)DIR_SEPARATOR;
937
938   /* Find such directory */
939   entry = memfs_find_entry_path(fs->root, path);
940   if (!entry) {
941     (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
942     return;
943   }
944
945   if (entry->directory || !entry->data) {
946     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
947     return;
948   }
949
950   /* Get real stat */
951 #ifdef SILC_WIN32
952   ret = stat(entry->data + 7, &stats);
953 #endif /* SILC_WIN32 */
954 #ifdef SILC_UNIX
955   ret = lstat(entry->data + 7, &stats);
956 #endif /* SILC_UNIX */
957 #ifdef SILC_SYMBIAN
958   ret = stat(entry->data + 7, &stats);
959 #endif /* SILC_SYMBIAN */
960   if (ret == -1) {
961     (*callback)(sftp, silc_sftp_map_errno(errno), NULL, callback_context);
962     return;
963   }
964
965   attrs = silc_calloc(1, sizeof(*attrs));
966   if (!attrs) {
967     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
968     return;
969   }
970   attrs->flags = (SILC_SFTP_ATTR_SIZE |
971                   SILC_SFTP_ATTR_UIDGID |
972                   SILC_SFTP_ATTR_ACMODTIME);
973   attrs->size = stats.st_size;
974   attrs->uid = 0;                   /* We use always 0 UID and GID */
975   attrs->gid = 0;
976   attrs->atime = stats.st_atime;
977   attrs->mtime = stats.st_mtime;
978
979   /* Return attributes */
980   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPAttributes)attrs,
981               callback_context);
982
983   silc_sftp_attr_free(attrs);
984 }
985
986 void memfs_fstat(void *context, SilcSFTP sftp,
987                  SilcSFTPHandle handle,
988                  SilcSFTPAttrCallback callback,
989                  void *callback_context)
990 {
991   MemFSFileHandle h = (MemFSFileHandle)handle;
992   SilcSFTPAttributes attrs;
993   int ret;
994   struct stat stats;
995
996   if (h->entry->directory || !h->entry->data) {
997     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
998     return;
999   }
1000
1001   /* Get real stat */
1002   ret = fstat(h->fd, &stats);
1003   if (ret == -1) {
1004     (*callback)(sftp, silc_sftp_map_errno(errno), NULL, callback_context);
1005     return;
1006   }
1007
1008   attrs = silc_calloc(1, sizeof(*attrs));
1009   if (!attrs) {
1010     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
1011     return;
1012   }
1013   attrs->flags = (SILC_SFTP_ATTR_SIZE |
1014                   SILC_SFTP_ATTR_UIDGID |
1015                   SILC_SFTP_ATTR_ACMODTIME);
1016   attrs->size = stats.st_size;
1017   attrs->uid = 0;                   /* We use always 0 UID and GID */
1018   attrs->gid = 0;
1019   attrs->atime = stats.st_atime;
1020   attrs->mtime = stats.st_mtime;
1021
1022   /* Return attributes */
1023   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPAttributes)attrs,
1024               callback_context);
1025
1026   silc_sftp_attr_free(attrs);
1027 }
1028
1029 void memfs_setstat(void *context, SilcSFTP sftp,
1030                    const char *path,
1031                    SilcSFTPAttributes attrs,
1032                    SilcSFTPStatusCallback callback,
1033                    void *callback_context)
1034 {
1035   /* Setstat is not supported */
1036   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL,
1037               callback_context);
1038 }
1039
1040 void memfs_fsetstat(void *context, SilcSFTP sftp,
1041                     SilcSFTPHandle handle,
1042                     SilcSFTPAttributes attrs,
1043                     SilcSFTPStatusCallback callback,
1044                     void *callback_context)
1045 {
1046   /* Fsetstat is not supported */
1047   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL,
1048               callback_context);
1049 }
1050
1051 void memfs_readlink(void *context, SilcSFTP sftp,
1052                     const char *path,
1053                     SilcSFTPNameCallback callback,
1054                     void *callback_context)
1055 {
1056   /* Readlink is not supported */
1057   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL,
1058               callback_context);
1059 }
1060
1061 void memfs_symlink(void *context, SilcSFTP sftp,
1062                    const char *linkpath,
1063                    const char *targetpath,
1064                    SilcSFTPStatusCallback callback,
1065                    void *callback_context)
1066 {
1067   /* Symlink is not supported */
1068   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL,
1069               callback_context);
1070 }
1071
1072 void memfs_realpath(void *context, SilcSFTP sftp,
1073                     const char *path,
1074                     SilcSFTPNameCallback callback,
1075                     void *callback_context)
1076 {
1077   MemFS fs = (MemFS)context;
1078   char *realpath;
1079   SilcSFTPName name;
1080
1081   if (!path || !strlen(path))
1082     path = (const char *)DIR_SEPARATOR;
1083
1084   realpath = memfs_expand_path(fs->root, path);
1085   if (!realpath)
1086     goto fail;
1087
1088   name = silc_calloc(1, sizeof(*name));
1089   if (!name)
1090     goto fail;
1091
1092   name->filename = silc_calloc(1, sizeof(*name->filename));
1093   if (!name->filename)
1094     goto fail;
1095   name->filename[0] = realpath;
1096   name->long_filename = silc_calloc(1, sizeof(*name->long_filename));
1097   if (!name->long_filename)
1098     goto fail;
1099   name->long_filename[0] = realpath;
1100   name->attrs = silc_calloc(1, sizeof(*name->attrs));
1101   if (!name->attrs)
1102     goto fail;
1103   name->attrs[0] = silc_calloc(1, sizeof(*name->attrs[0]));
1104   if (!name->attrs[0])
1105     goto fail;
1106   name->count = 1;
1107
1108   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPName)name,
1109               callback_context);
1110
1111   silc_sftp_name_free(name);
1112   return;
1113
1114  fail:
1115   (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
1116 }
1117
1118 void memfs_extended(void *context, SilcSFTP sftp,
1119                     const char *request,
1120                     const unsigned char *data,
1121                     SilcUInt32 data_len,
1122                     SilcSFTPExtendedCallback callback,
1123                     void *callback_context)
1124 {
1125   /* Extended is not supported */
1126   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, 0,
1127               callback_context);
1128 }
1129
1130 const struct SilcSFTPFilesystemOpsStruct silc_sftp_fs_memory = {
1131   memfs_get_handle,
1132   memfs_encode_handle,
1133   memfs_open,
1134   memfs_close,
1135   memfs_read,
1136   memfs_write,
1137   memfs_remove,
1138   memfs_rename,
1139   memfs_mkdir,
1140   memfs_rmdir,
1141   memfs_opendir,
1142   memfs_readdir,
1143   memfs_stat,
1144   memfs_lstat,
1145   memfs_fstat,
1146   memfs_setstat,
1147   memfs_fsetstat,
1148   memfs_readlink,
1149   memfs_symlink,
1150   memfs_realpath,
1151   memfs_extended
1152 };