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