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 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   uint32 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   uint32 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   uint32 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                                  uint32 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                                uint32 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       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, uint32 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                               uint32 data_len)
436 {
437   MemFS fs = (MemFS)context;
438   uint32 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                                  uint32 *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 = open(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 = 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               uint64 offset, 
556               uint32 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 = 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                uint64 offset,
591                const unsigned char *data,
592                uint32 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 = 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 *)strdup("/");
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   unsigned long 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 #ifndef SILC_WIN32\r
734                 if (!lstat(entry->data + 7, &stats))
735 #else\r
736                 if (!stat(entry->data + 7, &stats))\r
737 #endif\r
738                         filesize = stats.st_size;\r
739
740     /* Long name format is:
741        drwx------   1   324210 Apr  8 08:40 mail/
742        1234567890 123 12345678 123456789012 */
743     snprintf(long_name, sizeof(long_name) - 1,
744              "%c%c%c%c------ %3d %8lu %12s %s%s",
745              (entry->directory ? 'd' : '-'),
746              ((entry->perm & SILC_SFTP_FS_PERM_READ) ? 'r' : '-'),
747              ((entry->perm & SILC_SFTP_FS_PERM_WRITE) ? 'w' : '-'),
748              ((entry->perm & SILC_SFTP_FS_PERM_EXEC) ? 'x' : '-'),
749              (entry->directory ? (int)entry->entry_count : 1),
750              filesize, date, entry->name,
751              (entry->directory ? "/" : 
752               ((entry->perm & SILC_SFTP_FS_PERM_EXEC) ? "*" : "")));
753
754     /* Add attributes */
755     attrs = silc_calloc(1, sizeof(*attrs));
756     attrs->flags = (SILC_SFTP_ATTR_SIZE |
757                     SILC_SFTP_ATTR_UIDGID);
758     attrs->size = filesize;
759     attrs->uid = 0;                 /* We use always 0 UID and GID */
760     attrs->gid = 0;
761     if (!entry->directory) {
762       attrs->flags |= SILC_SFTP_ATTR_ACMODTIME;
763       attrs->atime = stats.st_atime;
764       attrs->mtime = stats.st_mtime;
765     }
766
767     /* Add the name */
768     silc_sftp_name_add(name, entry->name, long_name, attrs);
769   }
770
771   /* If we didn't read all then udpate the index for next read */
772   if (i >= h->entry->entry_count)
773     h->fd = -1;
774   else
775     h->fd = i;
776
777   /* If names was not found then return EOF. */
778   if (name->count == 0) {
779     (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, callback_context);
780     silc_sftp_name_free(name);
781     return;
782   }
783
784   /* Return name(s) */
785   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPName)name,
786               callback_context);
787
788   silc_sftp_name_free(name);
789 }
790
791 void mem_stat(void *context, SilcSFTP sftp,
792               const char *path,
793               SilcSFTPAttrCallback callback,
794               void *callback_context)
795 {
796   MemFS fs = (MemFS)context;
797   MemFSEntry entry;
798   SilcSFTPAttributes attrs;
799   int ret;
800   struct stat stats;
801
802   if (!path || !strlen(path))
803     path = (const char *)strdup("/");
804
805   /* Find such directory */
806   entry = mem_find_entry_path(fs->root, path);
807   if (!entry) {
808     (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
809     return;
810   }
811
812   if (entry->directory || !entry->data) {
813     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
814     return;
815   }    
816
817   /* Get real stat */
818   ret = stat(entry->data + 7, &stats);
819   if (ret == -1) {
820     (*callback)(sftp, silc_sftp_map_errno(errno), NULL, callback_context);
821     return;
822   }
823
824   attrs = silc_calloc(1, sizeof(*attrs));
825   attrs->flags = (SILC_SFTP_ATTR_SIZE |
826                   SILC_SFTP_ATTR_UIDGID |
827                   SILC_SFTP_ATTR_ACMODTIME);
828   attrs->size = stats.st_size;
829   attrs->uid = 0;                   /* We use always 0 UID and GID */
830   attrs->gid = 0;
831   attrs->atime = stats.st_atime;
832   attrs->mtime = stats.st_mtime;
833
834   /* Return attributes */
835   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPAttributes)attrs, 
836               callback_context);
837
838   silc_sftp_attr_free(attrs);
839 }
840
841 void mem_lstat(void *context, SilcSFTP sftp,
842                const char *path,
843                SilcSFTPAttrCallback callback,
844                void *callback_context)
845 {
846   MemFS fs = (MemFS)context;
847   MemFSEntry entry;
848   SilcSFTPAttributes attrs;
849   int ret;
850   struct stat stats;
851
852   if (!path || !strlen(path))
853     path = (const char *)strdup("/");
854
855   /* Find such directory */
856   entry = mem_find_entry_path(fs->root, path);
857   if (!entry) {
858     (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
859     return;
860   }
861
862   if (entry->directory || !entry->data) {
863     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
864     return;
865   }    
866
867   /* Get real stat */
868 #ifndef SILC_WIN32\r
869   ret = lstat(entry->data + 7, &stats);
870 #else\r
871   ret = stat(entry->data + 7, &stats);\r
872 #endif\r
873   if (ret == -1) {
874     (*callback)(sftp, silc_sftp_map_errno(errno), NULL, callback_context);
875     return;
876   }
877
878   attrs = silc_calloc(1, sizeof(*attrs));
879   attrs->flags = (SILC_SFTP_ATTR_SIZE |
880                   SILC_SFTP_ATTR_UIDGID |
881                   SILC_SFTP_ATTR_ACMODTIME);
882   attrs->size = stats.st_size;
883   attrs->uid = 0;                   /* We use always 0 UID and GID */
884   attrs->gid = 0;
885   attrs->atime = stats.st_atime;
886   attrs->mtime = stats.st_mtime;
887
888   /* Return attributes */
889   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPAttributes)attrs, 
890               callback_context);
891
892   silc_sftp_attr_free(attrs);
893 }
894
895 void mem_fstat(void *context, SilcSFTP sftp,
896                SilcSFTPHandle handle,
897                SilcSFTPAttrCallback callback,
898                void *callback_context)
899 {
900   MemFSFileHandle h = (MemFSFileHandle)handle;
901   SilcSFTPAttributes attrs;
902   int ret;
903   struct stat stats;
904
905   if (h->entry->directory || !h->entry->data) {
906     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
907     return;
908   }    
909
910   /* Get real stat */
911   ret = fstat(h->fd, &stats);
912   if (ret == -1) {
913     (*callback)(sftp, silc_sftp_map_errno(errno), NULL, callback_context);
914     return;
915   }
916
917   attrs = silc_calloc(1, sizeof(*attrs));
918   attrs->flags = (SILC_SFTP_ATTR_SIZE |
919                   SILC_SFTP_ATTR_UIDGID |
920                   SILC_SFTP_ATTR_ACMODTIME);
921   attrs->size = stats.st_size;
922   attrs->uid = 0;                   /* We use always 0 UID and GID */
923   attrs->gid = 0;
924   attrs->atime = stats.st_atime;
925   attrs->mtime = stats.st_mtime;
926
927   /* Return attributes */
928   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPAttributes)attrs, 
929               callback_context);
930
931   silc_sftp_attr_free(attrs);
932 }
933      
934 void mem_setstat(void *context, SilcSFTP sftp,
935                  const char *path,
936                  SilcSFTPAttributes attrs,
937                  SilcSFTPStatusCallback callback,
938                  void *callback_context)
939 {
940   /* Setstat is not supported */
941   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL, 
942               callback_context);
943 }
944
945 void mem_fsetstat(void *context, SilcSFTP sftp,
946                   SilcSFTPHandle handle,
947                   SilcSFTPAttributes attrs,
948                   SilcSFTPStatusCallback callback,
949                   void *callback_context)
950 {
951   /* Fsetstat is not supported */
952   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL, 
953               callback_context);
954 }
955
956 void mem_readlink(void *context, SilcSFTP sftp,
957                   const char *path,
958                   SilcSFTPNameCallback callback,
959                   void *callback_context)
960 {
961   /* Readlink is not supported */
962   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL,
963               callback_context);
964 }
965
966 void mem_symlink(void *context, SilcSFTP sftp,
967                  const char *linkpath,
968                  const char *targetpath,
969                  SilcSFTPStatusCallback callback,
970                  void *callback_context)
971 {
972   /* Symlink is not supported */
973   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL, 
974               callback_context);
975 }
976
977 void mem_realpath(void *context, SilcSFTP sftp,
978                   const char *path,
979                   SilcSFTPNameCallback callback,
980                   void *callback_context)
981 {
982   MemFS fs = (MemFS)context;
983   char *realpath;
984   SilcSFTPName name;
985
986   if (!path || !strlen(path))
987     path = (const char *)strdup("/");
988
989   realpath = mem_expand_path(fs->root, path);
990   if (!realpath) {
991     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
992     return;
993   }
994
995   name = silc_calloc(1, sizeof(*name));
996   name->filename = silc_calloc(1, sizeof(*name->filename));
997   name->filename[0] = realpath;
998   name->long_filename = silc_calloc(1, sizeof(*name->long_filename));
999   name->long_filename[0] = realpath;
1000   name->attrs = silc_calloc(1, sizeof(*name->attrs));
1001   name->attrs[0] = silc_calloc(1, sizeof(*name->attrs[0]));
1002   name->count = 1;
1003
1004   (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, (const SilcSFTPName)name, 
1005               callback_context);
1006
1007   silc_sftp_name_free(name);
1008 }
1009
1010 void mem_extended(void *context, SilcSFTP sftp,
1011                   const char *request,
1012                   const unsigned char *data,
1013                   uint32 data_len,
1014                   SilcSFTPExtendedCallback callback,
1015                   void *callback_context)
1016 {
1017   /* Extended is not supported */
1018   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, 0, 
1019               callback_context);
1020 }
1021
1022 struct SilcSFTPFilesystemOpsStruct silc_sftp_fs_memory = {
1023   mem_get_handle,
1024   mem_encode_handle,
1025   mem_open,
1026   mem_close,
1027   mem_read,
1028   mem_write,
1029   mem_remove,
1030   mem_rename,
1031   mem_mkdir,
1032   mem_rmdir,
1033   mem_opendir,
1034   mem_readdir,
1035   mem_stat,
1036   mem_lstat,
1037   mem_fstat,
1038   mem_setstat,
1039   mem_fsetstat,
1040   mem_readlink,
1041   mem_symlink,
1042   mem_realpath,
1043   mem_extended
1044 };