Changed SILC code to use new SRT and SCT APIs.
[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 - 2008 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
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 = memfs_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 SilcBool memfs_del_entry_name(MemFSEntry dir, const char *name,
211                                      SilcUInt32 name_len, SilcBool 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 = memfs_find_entry(dir, name, name_len);
220
221   if (entry)
222     return memfs_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 memfs_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 SilcBool memfs_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 memfs_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   if (!fs->root->name) {
339     silc_free(fs->root);
340     silc_free(fs);
341   }
342
343   filesystem = silc_calloc(1, sizeof(*filesystem));
344   if (!filesystem) {
345     silc_free(fs->root->name);
346     silc_free(fs->root);
347     silc_free(fs);
348     return NULL;
349   }
350
351   filesystem->fs = (struct SilcSFTPFilesystemOpsStruct *)&silc_sftp_fs_memory;
352   filesystem->fs_context = (void *)fs;
353
354   return filesystem;
355 }
356
357 /* Frees the memory filesystem context. */
358
359 void silc_sftp_fs_memory_free(SilcSFTPFilesystem fs)
360 {
361   MemFS memfs = (MemFS)fs->fs_context;
362
363   silc_free(memfs->root);
364   silc_free(memfs);
365 }
366
367 /* Adds a new directory to the memory filesystem. Returns the directory
368    context that can be used to add for example files to the directory
369    or new subdirectories under the directory. The `dir' is the parent
370    directory of the directory to be added. If this directory is to be
371    added to the root directory the `dir' is NULL.  The `name' is the name
372    of the directory. If error occurs this returns NULL. The caller must
373    not free the returned context. The `perm' will indicate the permissions
374    for the directory and they work in POSIX style. */
375
376 void *silc_sftp_fs_memory_add_dir(SilcSFTPFilesystem fs, void *dir,
377                                   SilcSFTPFSMemoryPerm perm,
378                                   const char *name)
379 {
380   MemFS memfs = (MemFS)fs->fs_context;
381   MemFSEntry entry;
382
383   entry = silc_calloc(1, sizeof(*entry));
384   if (!entry)
385     return NULL;
386
387   entry->perm = perm;
388   entry->directory = TRUE;
389   entry->parent = dir ? dir : memfs->root;
390   entry->name = strdup(name);
391   if (!entry->name) {
392     silc_free(entry);
393     return NULL;
394   }
395
396   if (!memfs_add_entry(dir ? dir : memfs->root, entry, FALSE)) {
397     silc_free(entry->name);
398     silc_free(entry);
399     return NULL;
400   }
401
402   return entry;
403 }
404
405 /* Deletes a directory indicated by the `dir'. All files and subdirectories
406    in this directory is also removed.  If the `dir' is NULL then all
407    directories and files are removed from the filesystem. Returns TRUE
408    if the removing was success. This is the only way to remove directories
409    in memory file system. The filesystem does not allow removing directories
410    with remote access using the filesystem access function sftp_rmdir. */
411
412 SilcBool silc_sftp_fs_memory_del_dir(SilcSFTPFilesystem fs, void *dir)
413 {
414   MemFS memfs = (MemFS)fs->fs_context;
415   SilcBool ret;
416
417   if (dir)
418     return memfs_del_entry(dir, FALSE);
419
420   /* Remove from root */
421   ret = memfs_del_entry(memfs->root, FALSE);
422
423   memfs->root = silc_calloc(1, sizeof(*memfs->root));
424   if (!memfs->root)
425     return FALSE;
426
427   memfs->root->perm = memfs->root_perm;
428   memfs->root->directory = TRUE;
429   memfs->root->name = strdup(DIR_SEPARATOR);
430   if (!memfs->root->name) {
431     silc_free(memfs->root);
432     memfs->root = NULL;
433     return FALSE;
434   }
435
436   return ret;
437 }
438
439 /* Adds a new file to the directory indicated by the `dir'.  If the `dir'
440    is NULL the file is added to the root directory. The `filename' is the
441    filename in the directory. The `realpath' is the real filepath in the
442    physical filesystem. It is used to actually access the file from the
443    memory filesystem. The `perm' will indicate the permissions for th e
444    file and they work in POSIX style. Returns TRUE if the file was
445    added to the directory. */
446
447 SilcBool silc_sftp_fs_memory_add_file(SilcSFTPFilesystem fs, void *dir,
448                                       SilcSFTPFSMemoryPerm perm,
449                                       const char *filename,
450                                       const char *realpath)
451 {
452   MemFS memfs = (MemFS)fs->fs_context;
453   MemFSEntry entry;
454
455   entry = silc_calloc(1, sizeof(*entry));
456   if (!entry)
457     return FALSE;
458
459   entry->perm = perm;
460   entry->directory = FALSE;
461   entry->name = strdup(filename);
462   entry->data = strdup(realpath);
463   if (!entry->name || !entry->data) {
464     silc_free(entry->name);
465     silc_free(entry->data);
466     silc_free(entry);
467     return FALSE;
468   }
469
470   return memfs_add_entry(dir ? dir : memfs->root, entry, FALSE);
471 }
472
473 /* Removes a file indicated by the `filename' from the directory
474    indicated by the `dir'. Returns TRUE if the removing was success. */
475
476 SilcBool silc_sftp_fs_memory_del_file(SilcSFTPFilesystem fs, void *dir,
477                                       const char *filename)
478 {
479   MemFS memfs = (MemFS)fs->fs_context;
480
481   if (!filename)
482     return FALSE;
483
484   return memfs_del_entry_name(dir ? dir : memfs->root, filename,
485                             strlen(filename), FALSE);
486 }
487
488 SilcSFTPHandle memfs_get_handle(void *context, SilcSFTP sftp,
489                                 const unsigned char *data,
490                                 SilcUInt32 data_len)
491 {
492   MemFS fs = (MemFS)context;
493   SilcUInt32 handle;
494
495   if (data_len < 4)
496     return NULL;
497
498   SILC_GET32_MSB(handle, data);
499   return (SilcSFTPHandle)memfs_find_handle(fs, handle);
500 }
501
502 unsigned char *memfs_encode_handle(void *context, SilcSFTP sftp,
503                                    SilcSFTPHandle handle,
504                                    SilcUInt32 *handle_len)
505 {
506   unsigned char *data;
507   MemFSFileHandle h = (MemFSFileHandle)handle;
508
509   data = silc_calloc(4, sizeof(*data));
510   if (!data)
511     return NULL;
512
513   SILC_PUT32_MSB(h->handle, data);
514   *handle_len = 4;
515   return data;
516 }
517
518 void memfs_open(void *context, SilcSFTP sftp,
519                 const char *filename,
520                 SilcSFTPFileOperation pflags,
521                 SilcSFTPAttributes attrs,
522                 SilcSFTPHandleCallback callback,
523                 void *callback_context)
524 {
525   MemFS fs = (MemFS)context;
526   MemFSEntry entry;
527   MemFSFileHandle handle;
528   int flags = 0, fd;
529
530   /* CREAT and TRUNC not supported */
531   if ((pflags & SILC_SFTP_FXF_CREAT) || (pflags & SILC_SFTP_FXF_TRUNC)) {
532     (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, callback_context);
533     return;
534   }
535
536   /* Find such file */
537   entry = memfs_find_entry_path(fs->root, filename);
538   if (!entry) {
539     (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
540     return;
541   }
542
543   if (entry->directory || !entry->data) {
544     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
545     return;
546   }
547
548   /* Check for reading */
549   if ((pflags & SILC_SFTP_FXF_READ) &&
550       !(entry->perm & SILC_SFTP_FS_PERM_READ)) {
551     (*callback)(sftp, SILC_SFTP_STATUS_PERMISSION_DENIED, NULL,
552                 callback_context);
553     return;
554   }
555
556   /* Check for writing */
557   if (((pflags & SILC_SFTP_FXF_WRITE) || (pflags & SILC_SFTP_FXF_APPEND)) &&
558       !(entry->perm & SILC_SFTP_FS_PERM_WRITE)) {
559     (*callback)(sftp, SILC_SFTP_STATUS_PERMISSION_DENIED, NULL,
560                 callback_context);
561     return;
562   }
563
564   if ((pflags & SILC_SFTP_FXF_READ) && (pflags & SILC_SFTP_FXF_WRITE))
565     flags = O_RDWR;
566   else if (pflags & SILC_SFTP_FXF_READ)
567     flags = O_RDONLY;
568   else if (pflags & SILC_SFTP_FXF_WRITE)
569     flags = O_WRONLY;
570   if (pflags & SILC_SFTP_FXF_APPEND)
571     flags |= O_APPEND;
572
573   /* Attempt to open the file for real. */
574   fd = silc_file_open_mode(entry->data + 7, flags,
575                            (attrs->flags & SILC_SFTP_ATTR_PERMISSIONS ?
576                             attrs->permissions : 0600));
577   if (fd == -1) {
578     (*callback)(sftp, silc_sftp_map_errno(silc_errno), NULL, callback_context);
579     return;
580   }
581
582   /* File opened, return handle */
583   handle = memfs_create_handle(fs, fd, entry);
584   if (handle)
585     (*callback)(sftp, SILC_SFTP_STATUS_OK, (SilcSFTPHandle)handle,
586                 callback_context);
587   else
588     (*callback)(sftp, SILC_SFTP_STATUS_PERMISSION_DENIED, NULL,
589                 callback_context);
590 }
591
592 void memfs_close(void *context, SilcSFTP sftp,
593                  SilcSFTPHandle handle,
594                  SilcSFTPStatusCallback callback,
595                  void *callback_context)
596 {
597   MemFS fs = (MemFS)context;
598   MemFSFileHandle h = (MemFSFileHandle)handle;
599   int ret;
600
601   if (h->fd != -1) {
602     ret = silc_file_close(h->fd);
603     if (ret == -1) {
604       (*callback)(sftp, silc_sftp_map_errno(silc_errno), NULL, NULL,
605                   callback_context);
606       return;
607     }
608   }
609
610   memfs_del_handle(fs, h);
611   (*callback)(sftp, SILC_SFTP_STATUS_OK, NULL, NULL, callback_context);
612 }
613
614 void memfs_read(void *context, SilcSFTP sftp,
615                 SilcSFTPHandle handle,
616                 SilcUInt64 offset,
617                 SilcUInt32 len,
618                 SilcSFTPDataCallback callback,
619                 void *callback_context)
620 {
621   MemFSFileHandle h = (MemFSFileHandle)handle;
622   unsigned char data[63488];
623   int ret;
624
625   if (len > 63488)
626     len = 63488;
627
628   ret = lseek(h->fd, (off_t)offset, SEEK_SET);
629   if (ret < 0) {
630     if (!ret)
631       (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, 0, callback_context);
632     else
633       (*callback)(sftp, silc_sftp_map_errno(silc_errno),
634                   NULL, 0, callback_context);
635     return;
636   }
637
638   /* Attempt to read */
639   ret = silc_file_read(h->fd, data, len);
640   if (ret <= 0) {
641     if (!ret)
642       (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, 0, callback_context);
643     else
644       (*callback)(sftp, silc_sftp_map_errno(silc_errno),
645                   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(silc_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   SilcFileStatStruct 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       silc_file_stat(entry->data + 7, TRUE, &stats);
809     }
810
811     /* Long name format is:
812        drwx------   1   324210 Apr  8 08:40 mail/
813        1234567890 123 12345678 123456789012 */
814     silc_snprintf(long_name, sizeof(long_name) - 1,
815              "%c%c%c%c------ %3d %8llu %12s %s%s",
816              (entry->directory ? 'd' : '-'),
817              ((entry->perm & SILC_SFTP_FS_PERM_READ) ? 'r' : '-'),
818              ((entry->perm & SILC_SFTP_FS_PERM_WRITE) ? 'w' : '-'),
819              ((entry->perm & SILC_SFTP_FS_PERM_EXEC) ? 'x' : '-'),
820              (entry->directory ? (int)entry->entry_count : 1),
821 #ifndef SILC_WIN32
822              (unsigned long long)filesize,
823 #else
824              (unsigned long)filesize,
825 #endif
826              date, entry->name,
827              (entry->directory ? "/" :
828               ((entry->perm & SILC_SFTP_FS_PERM_EXEC) ? "*" : "")));
829
830     /* Add attributes */
831     attrs = silc_calloc(1, sizeof(*attrs));
832     if (!attrs) {
833       (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, callback_context);
834       return;
835     }
836     attrs->flags = (SILC_SFTP_ATTR_SIZE |
837                     SILC_SFTP_ATTR_UIDGID);
838     attrs->size = filesize;
839     attrs->uid = 0;                 /* We use always 0 UID and GID */
840     attrs->gid = 0;
841     if (!entry->directory) {
842       attrs->flags |= SILC_SFTP_ATTR_ACMODTIME;
843       attrs->atime = silc_time_epoch(&stats.last_access);
844       attrs->mtime = silc_time_epoch(&stats.last_mod);
845     }
846
847     /* Add the name */
848     silc_sftp_name_add(name, entry->name, long_name, attrs);
849   }
850
851   /* If we didn't read all then udpate the index for next read */
852   if (i >= h->entry->entry_count)
853     h->fd = -1;
854   else
855     h->fd = i;
856
857   /* If names was not found then return EOF. */
858   if (name->count == 0) {
859     (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, callback_context);
860     silc_sftp_name_free(name);
861     return;
862   }
863
864   /* Return name(s) */
865   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPName)name,
866               callback_context);
867
868   silc_sftp_name_free(name);
869 }
870
871 void memfs_stat(void *context, SilcSFTP sftp,
872                 const char *path,
873                 SilcSFTPAttrCallback callback,
874                 void *callback_context)
875 {
876   MemFS fs = (MemFS)context;
877   MemFSEntry entry;
878   SilcSFTPAttributes attrs;
879   SilcFileStatStruct stats;
880
881   if (!path || !strlen(path))
882     path = (const char *)DIR_SEPARATOR;
883
884   /* Find such directory */
885   entry = memfs_find_entry_path(fs->root, path);
886   if (!entry) {
887     (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
888     return;
889   }
890
891   if (entry->directory || !entry->data) {
892     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
893     return;
894   }
895
896   /* Get real stat */
897   if (!silc_file_stat(entry->data + 7, TRUE, &stats)) {
898     (*callback)(sftp, silc_sftp_map_errno(silc_errno), NULL, callback_context);
899     return;
900   }
901
902   attrs = silc_calloc(1, sizeof(*attrs));
903   if (!attrs) {
904     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
905     return;
906   }
907   attrs->flags = (SILC_SFTP_ATTR_SIZE |
908                   SILC_SFTP_ATTR_UIDGID |
909                   SILC_SFTP_ATTR_ACMODTIME);
910   attrs->size = stats.size;
911   attrs->uid = 0;                   /* We use always 0 UID and GID */
912   attrs->gid = 0;
913   attrs->atime = silc_time_epoch(&stats.last_access);
914   attrs->mtime = silc_time_epoch(&stats.last_mod);
915
916   /* Return attributes */
917   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPAttributes)attrs,
918               callback_context);
919
920   silc_sftp_attr_free(attrs);
921 }
922
923 void memfs_lstat(void *context, SilcSFTP sftp,
924                  const char *path,
925                  SilcSFTPAttrCallback callback,
926                  void *callback_context)
927 {
928   MemFS fs = (MemFS)context;
929   MemFSEntry entry;
930   SilcSFTPAttributes attrs;
931   SilcFileStatStruct stats;
932
933   if (!path || !strlen(path))
934     path = (const char *)DIR_SEPARATOR;
935
936   /* Find such directory */
937   entry = memfs_find_entry_path(fs->root, path);
938   if (!entry) {
939     (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
940     return;
941   }
942
943   if (entry->directory || !entry->data) {
944     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
945     return;
946   }
947
948   /* Get real stat */
949   if (!silc_file_stat(entry->data + 7, FALSE, &stats)) {
950     (*callback)(sftp, silc_sftp_map_errno(silc_errno), NULL, callback_context);
951     return;
952   }
953
954   attrs = silc_calloc(1, sizeof(*attrs));
955   if (!attrs) {
956     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
957     return;
958   }
959   attrs->flags = (SILC_SFTP_ATTR_SIZE |
960                   SILC_SFTP_ATTR_UIDGID |
961                   SILC_SFTP_ATTR_ACMODTIME);
962   attrs->size = stats.size;
963   attrs->uid = 0;                   /* We use always 0 UID and GID */
964   attrs->gid = 0;
965   attrs->atime = silc_time_epoch(&stats.last_access);
966   attrs->mtime = silc_time_epoch(&stats.last_mod);
967
968   /* Return attributes */
969   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPAttributes)attrs,
970               callback_context);
971
972   silc_sftp_attr_free(attrs);
973 }
974
975 void memfs_fstat(void *context, SilcSFTP sftp,
976                  SilcSFTPHandle handle,
977                  SilcSFTPAttrCallback callback,
978                  void *callback_context)
979 {
980   MemFSFileHandle h = (MemFSFileHandle)handle;
981   SilcSFTPAttributes attrs;
982   SilcFileStatStruct stats;
983
984   if (h->entry->directory || !h->entry->data) {
985     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
986     return;
987   }
988
989   /* Get real stat */
990   if (!silc_file_fstat(h->fd, &stats)) {
991     (*callback)(sftp, silc_sftp_map_errno(silc_errno), NULL, callback_context);
992     return;
993   }
994
995   attrs = silc_calloc(1, sizeof(*attrs));
996   if (!attrs) {
997     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
998     return;
999   }
1000   attrs->flags = (SILC_SFTP_ATTR_SIZE |
1001                   SILC_SFTP_ATTR_UIDGID |
1002                   SILC_SFTP_ATTR_ACMODTIME);
1003   attrs->size = stats.size;
1004   attrs->uid = 0;                   /* We use always 0 UID and GID */
1005   attrs->gid = 0;
1006   attrs->atime = silc_time_epoch(&stats.last_access);
1007   attrs->mtime = silc_time_epoch(&stats.last_mod);
1008
1009   /* Return attributes */
1010   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPAttributes)attrs,
1011               callback_context);
1012
1013   silc_sftp_attr_free(attrs);
1014 }
1015
1016 void memfs_setstat(void *context, SilcSFTP sftp,
1017                    const char *path,
1018                    SilcSFTPAttributes attrs,
1019                    SilcSFTPStatusCallback callback,
1020                    void *callback_context)
1021 {
1022   /* Setstat is not supported */
1023   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL,
1024               callback_context);
1025 }
1026
1027 void memfs_fsetstat(void *context, SilcSFTP sftp,
1028                     SilcSFTPHandle handle,
1029                     SilcSFTPAttributes attrs,
1030                     SilcSFTPStatusCallback callback,
1031                     void *callback_context)
1032 {
1033   /* Fsetstat is not supported */
1034   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL,
1035               callback_context);
1036 }
1037
1038 void memfs_readlink(void *context, SilcSFTP sftp,
1039                     const char *path,
1040                     SilcSFTPNameCallback callback,
1041                     void *callback_context)
1042 {
1043   /* Readlink is not supported */
1044   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL,
1045               callback_context);
1046 }
1047
1048 void memfs_symlink(void *context, SilcSFTP sftp,
1049                    const char *linkpath,
1050                    const char *targetpath,
1051                    SilcSFTPStatusCallback callback,
1052                    void *callback_context)
1053 {
1054   /* Symlink is not supported */
1055   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL,
1056               callback_context);
1057 }
1058
1059 void memfs_realpath(void *context, SilcSFTP sftp,
1060                     const char *path,
1061                     SilcSFTPNameCallback callback,
1062                     void *callback_context)
1063 {
1064   MemFS fs = (MemFS)context;
1065   char *realpath;
1066   SilcSFTPName name;
1067
1068   if (!path || !strlen(path))
1069     path = (const char *)DIR_SEPARATOR;
1070
1071   realpath = memfs_expand_path(fs->root, path);
1072   if (!realpath)
1073     goto fail;
1074
1075   name = silc_calloc(1, sizeof(*name));
1076   if (!name)
1077     goto fail;
1078
1079   name->filename = silc_calloc(1, sizeof(*name->filename));
1080   if (!name->filename)
1081     goto fail;
1082   name->filename[0] = realpath;
1083   name->long_filename = silc_calloc(1, sizeof(*name->long_filename));
1084   if (!name->long_filename)
1085     goto fail;
1086   name->long_filename[0] = realpath;
1087   name->attrs = silc_calloc(1, sizeof(*name->attrs));
1088   if (!name->attrs)
1089     goto fail;
1090   name->attrs[0] = silc_calloc(1, sizeof(*name->attrs[0]));
1091   if (!name->attrs[0])
1092     goto fail;
1093   name->count = 1;
1094
1095   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPName)name,
1096               callback_context);
1097
1098   silc_sftp_name_free(name);
1099   return;
1100
1101  fail:
1102   (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
1103 }
1104
1105 void memfs_extended(void *context, SilcSFTP sftp,
1106                     const char *request,
1107                     const unsigned char *data,
1108                     SilcUInt32 data_len,
1109                     SilcSFTPExtendedCallback callback,
1110                     void *callback_context)
1111 {
1112   /* Extended is not supported */
1113   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, 0,
1114               callback_context);
1115 }
1116
1117 const struct SilcSFTPFilesystemOpsStruct silc_sftp_fs_memory = {
1118   memfs_get_handle,
1119   memfs_encode_handle,
1120   memfs_open,
1121   memfs_close,
1122   memfs_read,
1123   memfs_write,
1124   memfs_remove,
1125   memfs_rename,
1126   memfs_mkdir,
1127   memfs_rmdir,
1128   memfs_opendir,
1129   memfs_readdir,
1130   memfs_stat,
1131   memfs_lstat,
1132   memfs_fstat,
1133   memfs_setstat,
1134   memfs_fsetstat,
1135   memfs_readlink,
1136   memfs_symlink,
1137   memfs_realpath,
1138   memfs_extended
1139 };