ef6eb230c7c08defdba9c23c88b9d6d093a21555
[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
785     /* Long name format is:
786        drwx------   1   324210 Apr  8 08:40 mail/
787        1234567890 123 12345678 123456789012 */
788     snprintf(long_name, sizeof(long_name) - 1,
789              "%c%c%c%c------ %3d %8llu %12s %s%s",
790              (entry->directory ? 'd' : '-'),
791              ((entry->perm & SILC_SFTP_FS_PERM_READ) ? 'r' : '-'),
792              ((entry->perm & SILC_SFTP_FS_PERM_WRITE) ? 'w' : '-'),
793              ((entry->perm & SILC_SFTP_FS_PERM_EXEC) ? 'x' : '-'),
794              (entry->directory ? (int)entry->entry_count : 1),
795              filesize, date, entry->name,
796              (entry->directory ? "/" : 
797               ((entry->perm & SILC_SFTP_FS_PERM_EXEC) ? "*" : "")));
798
799     /* Add attributes */
800     attrs = silc_calloc(1, sizeof(*attrs));
801     if (!attrs) {
802       (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, callback_context);
803       return;
804     }
805     attrs->flags = (SILC_SFTP_ATTR_SIZE |
806                     SILC_SFTP_ATTR_UIDGID);
807     attrs->size = filesize;
808     attrs->uid = 0;                 /* We use always 0 UID and GID */
809     attrs->gid = 0;
810     if (!entry->directory) {
811       attrs->flags |= SILC_SFTP_ATTR_ACMODTIME;
812       attrs->atime = stats.st_atime;
813       attrs->mtime = stats.st_mtime;
814     }
815
816     /* Add the name */
817     silc_sftp_name_add(name, entry->name, long_name, attrs);
818   }
819
820   /* If we didn't read all then udpate the index for next read */
821   if (i >= h->entry->entry_count)
822     h->fd = -1;
823   else
824     h->fd = i;
825
826   /* If names was not found then return EOF. */
827   if (name->count == 0) {
828     (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, callback_context);
829     silc_sftp_name_free(name);
830     return;
831   }
832
833   /* Return name(s) */
834   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPName)name,
835               callback_context);
836
837   silc_sftp_name_free(name);
838 }
839
840 void mem_stat(void *context, SilcSFTP sftp,
841               const char *path,
842               SilcSFTPAttrCallback callback,
843               void *callback_context)
844 {
845   MemFS fs = (MemFS)context;
846   MemFSEntry entry;
847   SilcSFTPAttributes attrs;
848   int ret;
849   struct stat stats;
850
851   if (!path || !strlen(path))
852     path = (const char *)DIR_SEPARATOR;
853
854   /* Find such directory */
855   entry = mem_find_entry_path(fs->root, path);
856   if (!entry) {
857     (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
858     return;
859   }
860
861   if (entry->directory || !entry->data) {
862     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
863     return;
864   }    
865
866   /* Get real stat */
867   ret = stat(entry->data + 7, &stats);
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   if (!attrs) {
875     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
876     return;
877   }    
878   attrs->flags = (SILC_SFTP_ATTR_SIZE |
879                   SILC_SFTP_ATTR_UIDGID |
880                   SILC_SFTP_ATTR_ACMODTIME);
881   attrs->size = stats.st_size;
882   attrs->uid = 0;                   /* We use always 0 UID and GID */
883   attrs->gid = 0;
884   attrs->atime = stats.st_atime;
885   attrs->mtime = stats.st_mtime;
886
887   /* Return attributes */
888   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPAttributes)attrs, 
889               callback_context);
890
891   silc_sftp_attr_free(attrs);
892 }
893
894 void mem_lstat(void *context, SilcSFTP sftp,
895                const char *path,
896                SilcSFTPAttrCallback callback,
897                void *callback_context)
898 {
899   MemFS fs = (MemFS)context;
900   MemFSEntry entry;
901   SilcSFTPAttributes attrs;
902   int ret;
903   struct stat stats;
904
905   if (!path || !strlen(path))
906     path = (const char *)DIR_SEPARATOR;
907
908   /* Find such directory */
909   entry = mem_find_entry_path(fs->root, path);
910   if (!entry) {
911     (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
912     return;
913   }
914
915   if (entry->directory || !entry->data) {
916     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
917     return;
918   }    
919
920   /* Get real stat */
921 #ifndef SILC_WIN32
922   ret = lstat(entry->data + 7, &stats);
923 #else
924   ret = stat(entry->data + 7, &stats);
925 #endif
926   if (ret == -1) {
927     (*callback)(sftp, silc_sftp_map_errno(errno), NULL, callback_context);
928     return;
929   }
930
931   attrs = silc_calloc(1, sizeof(*attrs));
932   if (!attrs) {
933     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
934     return;
935   }    
936   attrs->flags = (SILC_SFTP_ATTR_SIZE |
937                   SILC_SFTP_ATTR_UIDGID |
938                   SILC_SFTP_ATTR_ACMODTIME);
939   attrs->size = stats.st_size;
940   attrs->uid = 0;                   /* We use always 0 UID and GID */
941   attrs->gid = 0;
942   attrs->atime = stats.st_atime;
943   attrs->mtime = stats.st_mtime;
944
945   /* Return attributes */
946   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPAttributes)attrs, 
947               callback_context);
948
949   silc_sftp_attr_free(attrs);
950 }
951
952 void mem_fstat(void *context, SilcSFTP sftp,
953                SilcSFTPHandle handle,
954                SilcSFTPAttrCallback callback,
955                void *callback_context)
956 {
957   MemFSFileHandle h = (MemFSFileHandle)handle;
958   SilcSFTPAttributes attrs;
959   int ret;
960   struct stat stats;
961
962   if (h->entry->directory || !h->entry->data) {
963     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
964     return;
965   }    
966
967   /* Get real stat */
968   ret = fstat(h->fd, &stats);
969   if (ret == -1) {
970     (*callback)(sftp, silc_sftp_map_errno(errno), NULL, callback_context);
971     return;
972   }
973
974   attrs = silc_calloc(1, sizeof(*attrs));
975   if (!attrs) {
976     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
977     return;
978   }    
979   attrs->flags = (SILC_SFTP_ATTR_SIZE |
980                   SILC_SFTP_ATTR_UIDGID |
981                   SILC_SFTP_ATTR_ACMODTIME);
982   attrs->size = stats.st_size;
983   attrs->uid = 0;                   /* We use always 0 UID and GID */
984   attrs->gid = 0;
985   attrs->atime = stats.st_atime;
986   attrs->mtime = stats.st_mtime;
987
988   /* Return attributes */
989   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPAttributes)attrs, 
990               callback_context);
991
992   silc_sftp_attr_free(attrs);
993 }
994      
995 void mem_setstat(void *context, SilcSFTP sftp,
996                  const char *path,
997                  SilcSFTPAttributes attrs,
998                  SilcSFTPStatusCallback callback,
999                  void *callback_context)
1000 {
1001   /* Setstat is not supported */
1002   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL, 
1003               callback_context);
1004 }
1005
1006 void mem_fsetstat(void *context, SilcSFTP sftp,
1007                   SilcSFTPHandle handle,
1008                   SilcSFTPAttributes attrs,
1009                   SilcSFTPStatusCallback callback,
1010                   void *callback_context)
1011 {
1012   /* Fsetstat is not supported */
1013   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL, 
1014               callback_context);
1015 }
1016
1017 void mem_readlink(void *context, SilcSFTP sftp,
1018                   const char *path,
1019                   SilcSFTPNameCallback callback,
1020                   void *callback_context)
1021 {
1022   /* Readlink is not supported */
1023   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL,
1024               callback_context);
1025 }
1026
1027 void mem_symlink(void *context, SilcSFTP sftp,
1028                  const char *linkpath,
1029                  const char *targetpath,
1030                  SilcSFTPStatusCallback callback,
1031                  void *callback_context)
1032 {
1033   /* Symlink is not supported */
1034   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL, 
1035               callback_context);
1036 }
1037
1038 void mem_realpath(void *context, SilcSFTP sftp,
1039                   const char *path,
1040                   SilcSFTPNameCallback callback,
1041                   void *callback_context)
1042 {
1043   MemFS fs = (MemFS)context;
1044   char *realpath;
1045   SilcSFTPName name;
1046
1047   if (!path || !strlen(path))
1048     path = (const char *)DIR_SEPARATOR;
1049
1050   realpath = mem_expand_path(fs->root, path);
1051   if (!realpath)
1052     goto fail;
1053
1054   name = silc_calloc(1, sizeof(*name));
1055   if (!name)
1056     goto fail;
1057
1058   name->filename = silc_calloc(1, sizeof(*name->filename));
1059   if (!name->filename)
1060     goto fail;
1061   name->filename[0] = realpath;
1062   name->long_filename = silc_calloc(1, sizeof(*name->long_filename));
1063   if (!name->long_filename)
1064     goto fail;
1065   name->long_filename[0] = realpath;
1066   name->attrs = silc_calloc(1, sizeof(*name->attrs));
1067   if (!name->attrs)
1068     goto fail;
1069   name->attrs[0] = silc_calloc(1, sizeof(*name->attrs[0]));
1070   if (!name->attrs[0])
1071     goto fail;
1072   name->count = 1;
1073
1074   (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, (const SilcSFTPName)name, 
1075               callback_context);
1076
1077   silc_sftp_name_free(name);
1078   return;
1079
1080  fail:
1081   (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
1082 }
1083
1084 void mem_extended(void *context, SilcSFTP sftp,
1085                   const char *request,
1086                   const unsigned char *data,
1087                   SilcUInt32 data_len,
1088                   SilcSFTPExtendedCallback callback,
1089                   void *callback_context)
1090 {
1091   /* Extended is not supported */
1092   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, 0, 
1093               callback_context);
1094 }
1095
1096 const struct SilcSFTPFilesystemOpsStruct silc_sftp_fs_memory = {
1097   mem_get_handle,
1098   mem_encode_handle,
1099   mem_open,
1100   mem_close,
1101   mem_read,
1102   mem_write,
1103   mem_remove,
1104   mem_rename,
1105   mem_mkdir,
1106   mem_rmdir,
1107   mem_opendir,
1108   mem_readdir,
1109   mem_stat,
1110   mem_lstat,
1111   mem_fstat,
1112   mem_setstat,
1113   mem_fsetstat,
1114   mem_readlink,
1115   mem_symlink,
1116   mem_realpath,
1117   mem_extended
1118 };