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