Integer type name change.
[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 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 = &silc_sftp_fs_memory;
324   filesystem->fs_context = (void *)fs;
325
326   return filesystem;
327 }
328
329 /* Frees the memory filesystem context. */
330
331 void silc_sftp_fs_memory_free(SilcSFTPFilesystem fs)
332 {
333   MemFS memfs = (MemFS)fs->fs_context;
334
335   silc_free(memfs->root);
336   silc_free(memfs);
337 }
338
339 /* Adds a new directory to the memory filesystem. Returns the directory
340    context that can be used to add for example files to the directory
341    or new subdirectories under the directory. The `dir' is the parent
342    directory of the directory to be added. If this directory is to be
343    added to the root directory the `dir' is NULL.  The `name' is the name
344    of the directory. If error occurs this returns NULL. The caller must
345    not free the returned context. The `perm' will indicate the permissions
346    for the directory and they work in POSIX style. */
347
348 void *silc_sftp_fs_memory_add_dir(SilcSFTPFilesystem fs, void *dir,
349                                   SilcSFTPFSMemoryPerm perm,
350                                   const char *name)
351 {
352   MemFS memfs = (MemFS)fs->fs_context;
353   MemFSEntry entry;
354
355   entry = silc_calloc(1, sizeof(*entry));
356   entry->perm = perm;
357   entry->name = strdup(name);
358   entry->directory = TRUE;
359   entry->parent = dir ? dir : memfs->root;
360
361   if (!mem_add_entry(dir ? dir : memfs->root, entry, FALSE))
362     return NULL;
363
364   return entry;
365 }
366
367 /* Deletes a directory indicated by the `dir'. All files and subdirectories
368    in this directory is also removed.  If the `dir' is NULL then all
369    directories and files are removed from the filesystem. Returns TRUE
370    if the removing was success. This is the only way to remove directories
371    in memory file system. The filesystem does not allow removing directories
372    with remote access using the filesystem access function sftp_rmdir. */
373
374 bool silc_sftp_fs_memory_del_dir(SilcSFTPFilesystem fs, void *dir)
375 {
376   MemFS memfs = (MemFS)fs->fs_context;
377   bool ret;
378
379   if (dir)
380     return mem_del_entry(dir, FALSE);
381
382   /* Remove from root */
383   ret = mem_del_entry(memfs->root, FALSE);
384
385   memfs->root = silc_calloc(1, sizeof(*memfs->root));
386   memfs->root->perm = memfs->root_perm;
387   memfs->root->directory = TRUE;
388   memfs->root->name = strdup(DIR_SEPARATOR);
389
390   return ret;
391 }
392
393 /* Adds a new file to the directory indicated by the `dir'.  If the `dir'
394    is NULL the file is added to the root directory. The `filename' is the
395    filename in the directory. The `realpath' is the real filepath in the
396    physical filesystem. It is used to actually access the file from the
397    memory filesystem. The `perm' will indicate the permissions for th e
398    file and they work in POSIX style. Returns TRUE if the file was
399    added to the directory. */
400
401 bool silc_sftp_fs_memory_add_file(SilcSFTPFilesystem fs, void *dir,
402                                   SilcSFTPFSMemoryPerm perm,
403                                   const char *filename,
404                                   const char *realpath)
405 {
406   MemFS memfs = (MemFS)fs->fs_context;
407   MemFSEntry entry;
408
409   entry = silc_calloc(1, sizeof(*entry));
410   entry->perm = perm;
411   entry->name = strdup(filename);
412   entry->data = strdup(realpath);
413   entry->directory = FALSE;
414
415   return mem_add_entry(dir ? dir : memfs->root, entry, FALSE);
416 }
417
418 /* Removes a file indicated by the `filename' from the directory
419    indicated by the `dir'. Returns TRUE if the removing was success. */
420
421 bool silc_sftp_fs_memory_del_file(SilcSFTPFilesystem fs, void *dir,
422                                   const char *filename)
423 {
424   MemFS memfs = (MemFS)fs->fs_context;
425
426   if (!filename)
427     return FALSE;
428
429   return mem_del_entry_name(dir ? dir : memfs->root, filename, 
430                             strlen(filename), FALSE);
431 }
432
433 SilcSFTPHandle mem_get_handle(void *context, SilcSFTP sftp,
434                               const unsigned char *data,
435                               SilcUInt32 data_len)
436 {
437   MemFS fs = (MemFS)context;
438   SilcUInt32 handle;
439
440   if (data_len < 4)
441     return NULL;
442
443   SILC_GET32_MSB(handle, data);
444   return (SilcSFTPHandle)mem_find_handle(fs, handle);
445 }
446
447 unsigned char *mem_encode_handle(void *context, SilcSFTP sftp,
448                                  SilcSFTPHandle handle,
449                                  SilcUInt32 *handle_len)
450 {
451   unsigned char *data;
452   MemFSFileHandle h = (MemFSFileHandle)handle;
453
454   data = silc_calloc(4, sizeof(*data));
455   SILC_PUT32_MSB(h->handle, data);
456   *handle_len = 4;
457
458   return data;
459 }
460
461 void mem_open(void *context, SilcSFTP sftp, 
462               const char *filename,
463               SilcSFTPFileOperation pflags,
464               SilcSFTPAttributes attrs,
465               SilcSFTPHandleCallback callback,
466               void *callback_context)
467 {
468   MemFS fs = (MemFS)context;
469   MemFSEntry entry;
470   MemFSFileHandle handle;
471   int flags = 0, fd;
472
473   /* CREAT and TRUNC not supported */
474   if ((pflags & SILC_SFTP_FXF_CREAT) || (pflags & SILC_SFTP_FXF_TRUNC)) {
475     (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, callback_context);
476     return;
477   }
478
479   /* Find such file */
480   entry = mem_find_entry_path(fs->root, filename);
481   if (!entry) {
482     (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
483     return;
484   }
485
486   if (entry->directory || !entry->data) {
487     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
488     return;
489   }    
490
491   /* Check for reading */
492   if ((pflags & SILC_SFTP_FXF_READ) && 
493       !(entry->perm & SILC_SFTP_FS_PERM_READ)) {
494     (*callback)(sftp, SILC_SFTP_STATUS_PERMISSION_DENIED, NULL, 
495                 callback_context);
496     return;
497   }    
498
499   /* Check for writing */
500   if (((pflags & SILC_SFTP_FXF_WRITE) || (pflags & SILC_SFTP_FXF_APPEND)) && 
501       !(entry->perm & SILC_SFTP_FS_PERM_WRITE)) {
502     (*callback)(sftp, SILC_SFTP_STATUS_PERMISSION_DENIED, NULL, 
503                 callback_context);
504     return;
505   }
506
507   if ((pflags & SILC_SFTP_FXF_READ) && (pflags & SILC_SFTP_FXF_WRITE))
508     flags = O_RDWR;
509   else if (pflags & SILC_SFTP_FXF_READ)
510     flags = O_RDONLY;
511   else if (pflags & SILC_SFTP_FXF_WRITE)
512     flags = O_WRONLY;
513   if (pflags & SILC_SFTP_FXF_APPEND)
514     flags |= O_APPEND;
515
516   /* Attempt to open the file for real. */
517   fd = silc_file_open_mode(entry->data + 7, flags, 
518                            (attrs->flags & SILC_SFTP_ATTR_PERMISSIONS ?
519                             attrs->permissions : 0600));
520   if (fd == -1) {
521     (*callback)(sftp, silc_sftp_map_errno(errno), NULL, callback_context);
522     return;
523   }
524
525   /* File opened, return handle */
526   handle = mem_create_handle(fs, fd, entry);
527   (*callback)(sftp, SILC_SFTP_STATUS_OK, (SilcSFTPHandle)handle, 
528               callback_context);
529 }
530
531 void mem_close(void *context, SilcSFTP sftp,
532                SilcSFTPHandle handle,
533                SilcSFTPStatusCallback callback,
534                void *callback_context)
535 {
536   MemFS fs = (MemFS)context;
537   MemFSFileHandle h = (MemFSFileHandle)handle;
538   int ret;
539
540   if (h->fd != -1) {
541     ret = silc_file_close(h->fd);
542     if (ret == -1) {
543       (*callback)(sftp, silc_sftp_map_errno(errno), NULL, NULL, 
544                   callback_context);
545       return;
546     }
547   }
548
549   mem_del_handle(fs, h);
550   (*callback)(sftp, SILC_SFTP_STATUS_OK, NULL, NULL, callback_context);
551 }
552
553 void mem_read(void *context, SilcSFTP sftp,
554               SilcSFTPHandle handle,
555               SilcUInt64 offset, 
556               SilcUInt32 len,
557               SilcSFTPDataCallback callback,
558               void *callback_context)
559 {
560   MemFSFileHandle h = (MemFSFileHandle)handle;
561   unsigned char *data;
562   int ret;
563
564   if (len > 32768)
565     len = 32768;
566
567   data = silc_malloc(len);
568   lseek(h->fd, (off_t)offset, SEEK_SET);
569
570   /* Attempt to read */
571   ret = silc_file_read(h->fd, data, len);
572   if (ret <= 0) {
573     if (!ret)
574       (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, 0, callback_context);
575     else
576       (*callback)(sftp, silc_sftp_map_errno(errno), NULL, 0, callback_context);
577     silc_free(data);
578     return;
579   }
580
581   /* Return data */
582   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const unsigned char *)data, 
583               ret, callback_context);
584
585   silc_free(data);
586 }
587
588 void mem_write(void *context, SilcSFTP sftp,
589                SilcSFTPHandle handle,
590                SilcUInt64 offset,
591                const unsigned char *data,
592                SilcUInt32 data_len,
593                SilcSFTPStatusCallback callback,
594                void *callback_context)
595 {
596   MemFSFileHandle h = (MemFSFileHandle)handle;
597   int ret;
598
599   lseek(h->fd, (off_t)offset, SEEK_SET);
600
601   /* Attempt to write */
602   ret = silc_file_write(h->fd, data, data_len);
603   if (ret <= 0) {
604     (*callback)(sftp, silc_sftp_map_errno(errno), NULL, NULL, 
605                 callback_context);
606     return;
607   }
608
609   (*callback)(sftp, SILC_SFTP_STATUS_OK, NULL, NULL, callback_context);
610 }
611
612 void mem_remove(void *context, SilcSFTP sftp,
613                 const char *filename,
614                 SilcSFTPStatusCallback callback,
615                 void *callback_context)
616 {
617   /* Remove is not supported */
618   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL, 
619               callback_context);
620 }
621
622 void mem_rename(void *context, SilcSFTP sftp,
623                 const char *oldname,
624                 const char *newname,
625                 SilcSFTPStatusCallback callback,
626                 void *callback_context)
627 {
628   /* Rename is not supported */
629   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL, 
630               callback_context);
631 }
632
633 void mem_mkdir(void *context, SilcSFTP sftp,
634                const char *path,
635                SilcSFTPAttributes attrs,
636                SilcSFTPStatusCallback callback,
637                void *callback_context)
638 {
639   /* Mkdir is not supported */
640   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL, 
641               callback_context);
642 }
643
644 void mem_rmdir(void *context, SilcSFTP sftp,
645                const char *path,
646                SilcSFTPStatusCallback callback,
647                void *callback_context)
648 {
649   /* Rmdir is not supported */
650   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL, 
651               callback_context);
652 }
653
654 void mem_opendir(void *context, SilcSFTP sftp,
655                  const char *path,
656                  SilcSFTPHandleCallback callback,
657                  void *callback_context)
658 {
659   MemFS fs = (MemFS)context;
660   MemFSEntry entry;
661   MemFSFileHandle handle;
662
663   if (!path || !strlen(path))
664     path = (const char *)DIR_SEPARATOR;
665
666   /* Find such directory */
667   entry = mem_find_entry_path(fs->root, path);
668   if (!entry) {
669     (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
670     return;
671   }
672
673   if (!entry->directory) {
674     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
675     return;
676   }    
677
678   /* Must be read permissions to open a directory */
679   if (!(entry->perm & SILC_SFTP_FS_PERM_READ)) {
680     (*callback)(sftp, SILC_SFTP_STATUS_PERMISSION_DENIED, NULL, 
681                 callback_context);
682     return;
683   }
684
685   /* Directory opened, return handle */
686   handle = mem_create_handle(fs, 0, entry);
687   (*callback)(sftp, SILC_SFTP_STATUS_OK, (SilcSFTPHandle)handle, 
688               callback_context);
689 }
690
691 void mem_readdir(void *context, SilcSFTP sftp,
692                  SilcSFTPHandle handle,
693                  SilcSFTPNameCallback callback,
694                  void *callback_context)
695 {
696   MemFSFileHandle h = (MemFSFileHandle)handle;
697   MemFSEntry entry;
698   SilcSFTPName name;
699   SilcSFTPAttributes attrs;
700   int i;
701   char long_name[256];
702   SilcUInt64 filesize = 0;
703   char *date;
704   struct stat stats;
705
706   if (!h->entry->directory) {
707     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
708     return;
709   }
710
711   if (h->fd == -1) {
712     (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, callback_context);
713     return;
714   }
715
716   name = silc_calloc(1, sizeof(*name));
717   for (i = h->fd; i < 100 + h->fd; i++) {
718     if (i >= h->entry->entry_count)
719       break;
720
721     entry = h->entry->entry[i];
722     if (!entry)
723       continue;
724
725     filesize = sizeof(*entry);
726     memset(long_name, 0, sizeof(long_name));
727
728     date = ctime(&entry->created);
729     if (strrchr(date, ':'))
730       *strrchr(date, ':') = '\0';
731
732     if (!entry->directory)
733       filesize = silc_file_size(entry->data + 7);
734
735     /* Long name format is:
736        drwx------   1   324210 Apr  8 08:40 mail/
737        1234567890 123 12345678 123456789012 */
738     snprintf(long_name, sizeof(long_name) - 1,
739              "%c%c%c%c------ %3d %8llu %12s %s%s",
740              (entry->directory ? 'd' : '-'),
741              ((entry->perm & SILC_SFTP_FS_PERM_READ) ? 'r' : '-'),
742              ((entry->perm & SILC_SFTP_FS_PERM_WRITE) ? 'w' : '-'),
743              ((entry->perm & SILC_SFTP_FS_PERM_EXEC) ? 'x' : '-'),
744              (entry->directory ? (int)entry->entry_count : 1),
745              filesize, date, entry->name,
746              (entry->directory ? "/" : 
747               ((entry->perm & SILC_SFTP_FS_PERM_EXEC) ? "*" : "")));
748
749     /* Add attributes */
750     attrs = silc_calloc(1, sizeof(*attrs));
751     attrs->flags = (SILC_SFTP_ATTR_SIZE |
752                     SILC_SFTP_ATTR_UIDGID);
753     attrs->size = filesize;
754     attrs->uid = 0;                 /* We use always 0 UID and GID */
755     attrs->gid = 0;
756     if (!entry->directory) {
757       attrs->flags |= SILC_SFTP_ATTR_ACMODTIME;
758       attrs->atime = stats.st_atime;
759       attrs->mtime = stats.st_mtime;
760     }
761
762     /* Add the name */
763     silc_sftp_name_add(name, entry->name, long_name, attrs);
764   }
765
766   /* If we didn't read all then udpate the index for next read */
767   if (i >= h->entry->entry_count)
768     h->fd = -1;
769   else
770     h->fd = i;
771
772   /* If names was not found then return EOF. */
773   if (name->count == 0) {
774     (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, callback_context);
775     silc_sftp_name_free(name);
776     return;
777   }
778
779   /* Return name(s) */
780   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPName)name,
781               callback_context);
782
783   silc_sftp_name_free(name);
784 }
785
786 void mem_stat(void *context, SilcSFTP sftp,
787               const char *path,
788               SilcSFTPAttrCallback callback,
789               void *callback_context)
790 {
791   MemFS fs = (MemFS)context;
792   MemFSEntry entry;
793   SilcSFTPAttributes attrs;
794   int ret;
795   struct stat stats;
796
797   if (!path || !strlen(path))
798     path = (const char *)DIR_SEPARATOR;
799
800   /* Find such directory */
801   entry = mem_find_entry_path(fs->root, path);
802   if (!entry) {
803     (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
804     return;
805   }
806
807   if (entry->directory || !entry->data) {
808     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
809     return;
810   }    
811
812   /* Get real stat */
813   ret = stat(entry->data + 7, &stats);
814   if (ret == -1) {
815     (*callback)(sftp, silc_sftp_map_errno(errno), NULL, callback_context);
816     return;
817   }
818
819   attrs = silc_calloc(1, sizeof(*attrs));
820   attrs->flags = (SILC_SFTP_ATTR_SIZE |
821                   SILC_SFTP_ATTR_UIDGID |
822                   SILC_SFTP_ATTR_ACMODTIME);
823   attrs->size = stats.st_size;
824   attrs->uid = 0;                   /* We use always 0 UID and GID */
825   attrs->gid = 0;
826   attrs->atime = stats.st_atime;
827   attrs->mtime = stats.st_mtime;
828
829   /* Return attributes */
830   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPAttributes)attrs, 
831               callback_context);
832
833   silc_sftp_attr_free(attrs);
834 }
835
836 void mem_lstat(void *context, SilcSFTP sftp,
837                const char *path,
838                SilcSFTPAttrCallback callback,
839                void *callback_context)
840 {
841   MemFS fs = (MemFS)context;
842   MemFSEntry entry;
843   SilcSFTPAttributes attrs;
844   int ret;
845   struct stat stats;
846
847   if (!path || !strlen(path))
848     path = (const char *)DIR_SEPARATOR;
849
850   /* Find such directory */
851   entry = mem_find_entry_path(fs->root, path);
852   if (!entry) {
853     (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
854     return;
855   }
856
857   if (entry->directory || !entry->data) {
858     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
859     return;
860   }    
861
862   /* Get real stat */
863 #ifndef SILC_WIN32
864   ret = lstat(entry->data + 7, &stats);
865 #else
866   ret = stat(entry->data + 7, &stats);
867 #endif
868   if (ret == -1) {
869     (*callback)(sftp, silc_sftp_map_errno(errno), NULL, callback_context);
870     return;
871   }
872
873   attrs = silc_calloc(1, sizeof(*attrs));
874   attrs->flags = (SILC_SFTP_ATTR_SIZE |
875                   SILC_SFTP_ATTR_UIDGID |
876                   SILC_SFTP_ATTR_ACMODTIME);
877   attrs->size = stats.st_size;
878   attrs->uid = 0;                   /* We use always 0 UID and GID */
879   attrs->gid = 0;
880   attrs->atime = stats.st_atime;
881   attrs->mtime = stats.st_mtime;
882
883   /* Return attributes */
884   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPAttributes)attrs, 
885               callback_context);
886
887   silc_sftp_attr_free(attrs);
888 }
889
890 void mem_fstat(void *context, SilcSFTP sftp,
891                SilcSFTPHandle handle,
892                SilcSFTPAttrCallback callback,
893                void *callback_context)
894 {
895   MemFSFileHandle h = (MemFSFileHandle)handle;
896   SilcSFTPAttributes attrs;
897   int ret;
898   struct stat stats;
899
900   if (h->entry->directory || !h->entry->data) {
901     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
902     return;
903   }    
904
905   /* Get real stat */
906   ret = fstat(h->fd, &stats);
907   if (ret == -1) {
908     (*callback)(sftp, silc_sftp_map_errno(errno), NULL, callback_context);
909     return;
910   }
911
912   attrs = silc_calloc(1, sizeof(*attrs));
913   attrs->flags = (SILC_SFTP_ATTR_SIZE |
914                   SILC_SFTP_ATTR_UIDGID |
915                   SILC_SFTP_ATTR_ACMODTIME);
916   attrs->size = stats.st_size;
917   attrs->uid = 0;                   /* We use always 0 UID and GID */
918   attrs->gid = 0;
919   attrs->atime = stats.st_atime;
920   attrs->mtime = stats.st_mtime;
921
922   /* Return attributes */
923   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPAttributes)attrs, 
924               callback_context);
925
926   silc_sftp_attr_free(attrs);
927 }
928      
929 void mem_setstat(void *context, SilcSFTP sftp,
930                  const char *path,
931                  SilcSFTPAttributes attrs,
932                  SilcSFTPStatusCallback callback,
933                  void *callback_context)
934 {
935   /* Setstat is not supported */
936   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL, 
937               callback_context);
938 }
939
940 void mem_fsetstat(void *context, SilcSFTP sftp,
941                   SilcSFTPHandle handle,
942                   SilcSFTPAttributes attrs,
943                   SilcSFTPStatusCallback callback,
944                   void *callback_context)
945 {
946   /* Fsetstat is not supported */
947   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL, 
948               callback_context);
949 }
950
951 void mem_readlink(void *context, SilcSFTP sftp,
952                   const char *path,
953                   SilcSFTPNameCallback callback,
954                   void *callback_context)
955 {
956   /* Readlink is not supported */
957   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL,
958               callback_context);
959 }
960
961 void mem_symlink(void *context, SilcSFTP sftp,
962                  const char *linkpath,
963                  const char *targetpath,
964                  SilcSFTPStatusCallback callback,
965                  void *callback_context)
966 {
967   /* Symlink is not supported */
968   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL, 
969               callback_context);
970 }
971
972 void mem_realpath(void *context, SilcSFTP sftp,
973                   const char *path,
974                   SilcSFTPNameCallback callback,
975                   void *callback_context)
976 {
977   MemFS fs = (MemFS)context;
978   char *realpath;
979   SilcSFTPName name;
980
981   if (!path || !strlen(path))
982     path = (const char *)DIR_SEPARATOR;
983
984   realpath = mem_expand_path(fs->root, path);
985   if (!realpath) {
986     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
987     return;
988   }
989
990   name = silc_calloc(1, sizeof(*name));
991   name->filename = silc_calloc(1, sizeof(*name->filename));
992   name->filename[0] = realpath;
993   name->long_filename = silc_calloc(1, sizeof(*name->long_filename));
994   name->long_filename[0] = realpath;
995   name->attrs = silc_calloc(1, sizeof(*name->attrs));
996   name->attrs[0] = silc_calloc(1, sizeof(*name->attrs[0]));
997   name->count = 1;
998
999   (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, (const SilcSFTPName)name, 
1000               callback_context);
1001
1002   silc_sftp_name_free(name);
1003 }
1004
1005 void mem_extended(void *context, SilcSFTP sftp,
1006                   const char *request,
1007                   const unsigned char *data,
1008                   SilcUInt32 data_len,
1009                   SilcSFTPExtendedCallback callback,
1010                   void *callback_context)
1011 {
1012   /* Extended is not supported */
1013   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, 0, 
1014               callback_context);
1015 }
1016
1017 struct SilcSFTPFilesystemOpsStruct silc_sftp_fs_memory = {
1018   mem_get_handle,
1019   mem_encode_handle,
1020   mem_open,
1021   mem_close,
1022   mem_read,
1023   mem_write,
1024   mem_remove,
1025   mem_rename,
1026   mem_mkdir,
1027   mem_rmdir,
1028   mem_opendir,
1029   mem_readdir,
1030   mem_stat,
1031   mem_lstat,
1032   mem_fstat,
1033   mem_setstat,
1034   mem_fsetstat,
1035   mem_readlink,
1036   mem_symlink,
1037   mem_realpath,
1038   mem_extended
1039 };