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