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