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