Code and comment cleanup.
[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 *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 SilcBool mem_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 mem_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 (!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 SilcBool mem_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 = 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 SilcBool 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   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 (!mem_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 mem_del_entry(dir, FALSE);
419
420   /* Remove from root */
421   ret = mem_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 mem_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 mem_del_entry_name(dir ? dir : memfs->root, filename,
485                             strlen(filename), FALSE);
486 }
487
488 SilcSFTPHandle mem_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)mem_find_handle(fs, handle);
500 }
501
502 unsigned char *mem_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 mem_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 = mem_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(errno), NULL, callback_context);
579     return;
580   }
581
582   /* File opened, return handle */
583   handle = mem_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 mem_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(errno), NULL, NULL,
605                   callback_context);
606       return;
607     }
608   }
609
610   mem_del_handle(fs, h);
611   (*callback)(sftp, SILC_SFTP_STATUS_OK, NULL, NULL, callback_context);
612 }
613
614 void mem_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;
623   int ret;
624
625   if (len > 32768)
626     len = 32768;
627
628   data = silc_malloc(len);
629   if (!data) {
630     (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, 0, callback_context);
631     return;
632   }
633   lseek(h->fd, (off_t)offset, SEEK_SET);
634
635   /* Attempt to read */
636   ret = silc_file_read(h->fd, data, len);
637   if (ret <= 0) {
638     if (!ret)
639       (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, 0, callback_context);
640     else
641       (*callback)(sftp, silc_sftp_map_errno(errno), NULL, 0, callback_context);
642     silc_free(data);
643     return;
644   }
645
646   /* Return data */
647   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const unsigned char *)data,
648               ret, callback_context);
649
650   silc_free(data);
651 }
652
653 void mem_write(void *context, SilcSFTP sftp,
654                SilcSFTPHandle handle,
655                SilcUInt64 offset,
656                const unsigned char *data,
657                SilcUInt32 data_len,
658                SilcSFTPStatusCallback callback,
659                void *callback_context)
660 {
661   MemFSFileHandle h = (MemFSFileHandle)handle;
662   int ret;
663
664   lseek(h->fd, (off_t)offset, SEEK_SET);
665
666   /* Attempt to write */
667   ret = silc_file_write(h->fd, data, data_len);
668   if (ret <= 0) {
669     (*callback)(sftp, silc_sftp_map_errno(errno), NULL, NULL,
670                 callback_context);
671     return;
672   }
673
674   (*callback)(sftp, SILC_SFTP_STATUS_OK, NULL, NULL, callback_context);
675 }
676
677 void mem_remove(void *context, SilcSFTP sftp,
678                 const char *filename,
679                 SilcSFTPStatusCallback callback,
680                 void *callback_context)
681 {
682   /* Remove is not supported */
683   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL,
684               callback_context);
685 }
686
687 void mem_rename(void *context, SilcSFTP sftp,
688                 const char *oldname,
689                 const char *newname,
690                 SilcSFTPStatusCallback callback,
691                 void *callback_context)
692 {
693   /* Rename is not supported */
694   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL,
695               callback_context);
696 }
697
698 void mem_mkdir(void *context, SilcSFTP sftp,
699                const char *path,
700                SilcSFTPAttributes attrs,
701                SilcSFTPStatusCallback callback,
702                void *callback_context)
703 {
704   /* Mkdir is not supported */
705   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL,
706               callback_context);
707 }
708
709 void mem_rmdir(void *context, SilcSFTP sftp,
710                const char *path,
711                SilcSFTPStatusCallback callback,
712                void *callback_context)
713 {
714   /* Rmdir is not supported */
715   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL,
716               callback_context);
717 }
718
719 void mem_opendir(void *context, SilcSFTP sftp,
720                  const char *path,
721                  SilcSFTPHandleCallback callback,
722                  void *callback_context)
723 {
724   MemFS fs = (MemFS)context;
725   MemFSEntry entry;
726   MemFSFileHandle handle;
727
728   if (!path || !strlen(path))
729     path = (const char *)DIR_SEPARATOR;
730
731   /* Find such directory */
732   entry = mem_find_entry_path(fs->root, path);
733   if (!entry) {
734     (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
735     return;
736   }
737
738   if (!entry->directory) {
739     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
740     return;
741   }
742
743   /* Must be read permissions to open a directory */
744   if (!(entry->perm & SILC_SFTP_FS_PERM_READ)) {
745     (*callback)(sftp, SILC_SFTP_STATUS_PERMISSION_DENIED, NULL,
746                 callback_context);
747     return;
748   }
749
750   /* Directory opened, return handle */
751   handle = mem_create_handle(fs, 0, entry);
752   if (handle)
753     (*callback)(sftp, SILC_SFTP_STATUS_OK, (SilcSFTPHandle)handle,
754                 callback_context);
755   else
756     (*callback)(sftp, SILC_SFTP_STATUS_PERMISSION_DENIED, NULL,
757                 callback_context);
758 }
759
760 void mem_readdir(void *context, SilcSFTP sftp,
761                  SilcSFTPHandle handle,
762                  SilcSFTPNameCallback callback,
763                  void *callback_context)
764 {
765   MemFSFileHandle h = (MemFSFileHandle)handle;
766   MemFSEntry entry;
767   SilcSFTPName name;
768   SilcSFTPAttributes attrs;
769   int i;
770   char long_name[256];
771   SilcUInt64 filesize = 0;
772   char *date;
773   struct stat stats;
774
775   if (!h->entry->directory) {
776     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
777     return;
778   }
779
780   if (h->fd == -1) {
781     (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, callback_context);
782     return;
783   }
784
785   name = silc_calloc(1, sizeof(*name));
786   if (!name) {
787     (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, callback_context);
788     return;
789   }
790   for (i = h->fd; i < 100 + h->fd; i++) {
791     if (i >= h->entry->entry_count)
792       break;
793
794     entry = h->entry->entry[i];
795     if (!entry)
796       continue;
797
798     filesize = sizeof(*entry);
799     memset(long_name, 0, sizeof(long_name));
800
801     date = (char *)silc_time_string(entry->created);
802     if (strrchr(date, ':'))
803       *strrchr(date, ':') = '\0';
804
805     if (!entry->directory) {
806       filesize = silc_file_size(entry->data + 7);
807       memset(&stats, 0, sizeof(stats));
808       stat(entry->data + 7, &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 = stats.st_atime;
844       attrs->mtime = stats.st_mtime;
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 mem_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   int ret;
880   struct stat stats;
881
882   if (!path || !strlen(path))
883     path = (const char *)DIR_SEPARATOR;
884
885   /* Find such directory */
886   entry = mem_find_entry_path(fs->root, path);
887   if (!entry) {
888     (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
889     return;
890   }
891
892   if (entry->directory || !entry->data) {
893     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
894     return;
895   }
896
897   /* Get real stat */
898   ret = stat(entry->data + 7, &stats);
899   if (ret == -1) {
900     (*callback)(sftp, silc_sftp_map_errno(errno), NULL, callback_context);
901     return;
902   }
903
904   attrs = silc_calloc(1, sizeof(*attrs));
905   if (!attrs) {
906     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
907     return;
908   }
909   attrs->flags = (SILC_SFTP_ATTR_SIZE |
910                   SILC_SFTP_ATTR_UIDGID |
911                   SILC_SFTP_ATTR_ACMODTIME);
912   attrs->size = stats.st_size;
913   attrs->uid = 0;                   /* We use always 0 UID and GID */
914   attrs->gid = 0;
915   attrs->atime = stats.st_atime;
916   attrs->mtime = stats.st_mtime;
917
918   /* Return attributes */
919   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPAttributes)attrs,
920               callback_context);
921
922   silc_sftp_attr_free(attrs);
923 }
924
925 void mem_lstat(void *context, SilcSFTP sftp,
926                const char *path,
927                SilcSFTPAttrCallback callback,
928                void *callback_context)
929 {
930   MemFS fs = (MemFS)context;
931   MemFSEntry entry;
932   SilcSFTPAttributes attrs;
933   int ret;
934   struct stat stats;
935
936   if (!path || !strlen(path))
937     path = (const char *)DIR_SEPARATOR;
938
939   /* Find such directory */
940   entry = mem_find_entry_path(fs->root, path);
941   if (!entry) {
942     (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
943     return;
944   }
945
946   if (entry->directory || !entry->data) {
947     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
948     return;
949   }
950
951   /* Get real stat */
952 #ifndef SILC_WIN32
953   ret = lstat(entry->data + 7, &stats);
954 #else
955   ret = stat(entry->data + 7, &stats);
956 #endif
957   if (ret == -1) {
958     (*callback)(sftp, silc_sftp_map_errno(errno), NULL, callback_context);
959     return;
960   }
961
962   attrs = silc_calloc(1, sizeof(*attrs));
963   if (!attrs) {
964     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
965     return;
966   }
967   attrs->flags = (SILC_SFTP_ATTR_SIZE |
968                   SILC_SFTP_ATTR_UIDGID |
969                   SILC_SFTP_ATTR_ACMODTIME);
970   attrs->size = stats.st_size;
971   attrs->uid = 0;                   /* We use always 0 UID and GID */
972   attrs->gid = 0;
973   attrs->atime = stats.st_atime;
974   attrs->mtime = stats.st_mtime;
975
976   /* Return attributes */
977   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPAttributes)attrs,
978               callback_context);
979
980   silc_sftp_attr_free(attrs);
981 }
982
983 void mem_fstat(void *context, SilcSFTP sftp,
984                SilcSFTPHandle handle,
985                SilcSFTPAttrCallback callback,
986                void *callback_context)
987 {
988   MemFSFileHandle h = (MemFSFileHandle)handle;
989   SilcSFTPAttributes attrs;
990   int ret;
991   struct stat stats;
992
993   if (h->entry->directory || !h->entry->data) {
994     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
995     return;
996   }
997
998   /* Get real stat */
999   ret = fstat(h->fd, &stats);
1000   if (ret == -1) {
1001     (*callback)(sftp, silc_sftp_map_errno(errno), NULL, callback_context);
1002     return;
1003   }
1004
1005   attrs = silc_calloc(1, sizeof(*attrs));
1006   if (!attrs) {
1007     (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
1008     return;
1009   }
1010   attrs->flags = (SILC_SFTP_ATTR_SIZE |
1011                   SILC_SFTP_ATTR_UIDGID |
1012                   SILC_SFTP_ATTR_ACMODTIME);
1013   attrs->size = stats.st_size;
1014   attrs->uid = 0;                   /* We use always 0 UID and GID */
1015   attrs->gid = 0;
1016   attrs->atime = stats.st_atime;
1017   attrs->mtime = stats.st_mtime;
1018
1019   /* Return attributes */
1020   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPAttributes)attrs,
1021               callback_context);
1022
1023   silc_sftp_attr_free(attrs);
1024 }
1025
1026 void mem_setstat(void *context, SilcSFTP sftp,
1027                  const char *path,
1028                  SilcSFTPAttributes attrs,
1029                  SilcSFTPStatusCallback callback,
1030                  void *callback_context)
1031 {
1032   /* Setstat is not supported */
1033   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL,
1034               callback_context);
1035 }
1036
1037 void mem_fsetstat(void *context, SilcSFTP sftp,
1038                   SilcSFTPHandle handle,
1039                   SilcSFTPAttributes attrs,
1040                   SilcSFTPStatusCallback callback,
1041                   void *callback_context)
1042 {
1043   /* Fsetstat is not supported */
1044   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL,
1045               callback_context);
1046 }
1047
1048 void mem_readlink(void *context, SilcSFTP sftp,
1049                   const char *path,
1050                   SilcSFTPNameCallback callback,
1051                   void *callback_context)
1052 {
1053   /* Readlink is not supported */
1054   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL,
1055               callback_context);
1056 }
1057
1058 void mem_symlink(void *context, SilcSFTP sftp,
1059                  const char *linkpath,
1060                  const char *targetpath,
1061                  SilcSFTPStatusCallback callback,
1062                  void *callback_context)
1063 {
1064   /* Symlink is not supported */
1065   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL,
1066               callback_context);
1067 }
1068
1069 void mem_realpath(void *context, SilcSFTP sftp,
1070                   const char *path,
1071                   SilcSFTPNameCallback callback,
1072                   void *callback_context)
1073 {
1074   MemFS fs = (MemFS)context;
1075   char *realpath;
1076   SilcSFTPName name;
1077
1078   if (!path || !strlen(path))
1079     path = (const char *)DIR_SEPARATOR;
1080
1081   realpath = mem_expand_path(fs->root, path);
1082   if (!realpath)
1083     goto fail;
1084
1085   name = silc_calloc(1, sizeof(*name));
1086   if (!name)
1087     goto fail;
1088
1089   name->filename = silc_calloc(1, sizeof(*name->filename));
1090   if (!name->filename)
1091     goto fail;
1092   name->filename[0] = realpath;
1093   name->long_filename = silc_calloc(1, sizeof(*name->long_filename));
1094   if (!name->long_filename)
1095     goto fail;
1096   name->long_filename[0] = realpath;
1097   name->attrs = silc_calloc(1, sizeof(*name->attrs));
1098   if (!name->attrs)
1099     goto fail;
1100   name->attrs[0] = silc_calloc(1, sizeof(*name->attrs[0]));
1101   if (!name->attrs[0])
1102     goto fail;
1103   name->count = 1;
1104
1105   (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPName)name,
1106               callback_context);
1107
1108   silc_sftp_name_free(name);
1109   return;
1110
1111  fail:
1112   (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
1113 }
1114
1115 void mem_extended(void *context, SilcSFTP sftp,
1116                   const char *request,
1117                   const unsigned char *data,
1118                   SilcUInt32 data_len,
1119                   SilcSFTPExtendedCallback callback,
1120                   void *callback_context)
1121 {
1122   /* Extended is not supported */
1123   (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, 0,
1124               callback_context);
1125 }
1126
1127 const struct SilcSFTPFilesystemOpsStruct silc_sftp_fs_memory = {
1128   mem_get_handle,
1129   mem_encode_handle,
1130   mem_open,
1131   mem_close,
1132   mem_read,
1133   mem_write,
1134   mem_remove,
1135   mem_rename,
1136   mem_mkdir,
1137   mem_rmdir,
1138   mem_opendir,
1139   mem_readdir,
1140   mem_stat,
1141   mem_lstat,
1142   mem_fstat,
1143   mem_setstat,
1144   mem_fsetstat,
1145   mem_readlink,
1146   mem_symlink,
1147   mem_realpath,
1148   mem_extended
1149 };