5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 Pekka Riikonen
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.
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.
20 /* XXX TODO Win32 support */
22 #include "silcincludes.h"
24 #include "silcsftp_fs.h"
25 #include "sftp_util.h"
27 #define DIR_SEPARATOR "/"
29 /* Memory filesystem entry */
30 typedef struct MemFSEntryStruct {
31 char *name; /* Name of the entry */
32 char *data; /* Data of the entry */
33 bool directory; /* TRUE if this is directory */
34 SilcSFTPFSMemoryPerm perm; /* Permissions */
35 struct MemFSEntryStruct **entry; /* Files and sub-directories */
36 uint32 entry_count; /* Number of files and sub-directories */
37 struct MemFSEntryStruct *parent; /* non-NULL if `directory' is TRUE,
38 includes parent directory. */
39 unsigned long created; /* Time of creation */
44 uint32 handle; /* Handle index */
45 int fd; /* Real file handle */
46 MemFSEntry entry; /* Filesystem entry */
49 /* Memory filesystem */
51 MemFSEntry root; /* Root of the filesystem hierarchy */
52 SilcSFTPFSMemoryPerm root_perm;
53 MemFSFileHandle *handles; /* Open file handles */
57 /* Generates absolute path from relative path that may include '.' and '..'
60 static char *mem_expand_path(MemFSEntry root, const char *path)
62 if (!strstr(path, "./") && !strstr(path, "../") &&
63 !strstr(path, "/..") && !strstr(path, "/."))
70 /* Add `entry' to directory `dir'. */
72 static bool mem_add_entry(MemFSEntry dir, MemFSEntry entry,
77 /* Must be both write and exec permissions */
79 !((dir->perm & SILC_SFTP_FS_PERM_WRITE) &&
80 (dir->perm & SILC_SFTP_FS_PERM_EXEC)))
84 dir->entry = silc_calloc(3, sizeof(*entry));
85 dir->entry[0] = entry;
87 entry->created = time(0);
91 for (i = 0; i < dir->entry_count; i++) {
95 dir->entry[i] = entry;
96 entry->created = time(0);
100 dir->entry = silc_realloc(dir->entry, sizeof(*dir->entry) *
101 (dir->entry_count + 3));
102 for (i = dir->entry_count + 1; i < dir->entry_count + 3; i++)
103 dir->entry[i] = NULL;
104 dir->entry[dir->entry_count] = entry;
105 dir->entry_count += 3;
106 entry->created = time(0);
111 /* Removes entry `entry' and all entries under it recursively. */
113 static bool mem_del_entry(MemFSEntry entry, bool check_perm)
117 /* Directories cannot be removed from remote access */
121 silc_free(entry->name);
122 silc_free(entry->data);
124 /* Delete all entries recursively under this entry */
125 for (i = 0; i < entry->entry_count; i++) {
126 if (entry->entry[i]) {
127 if (!mem_del_entry(entry->entry[i], FALSE))
131 silc_free(entry->entry);
133 /* Remove from parent */
135 for (i = 0; i < entry->parent->entry_count; i++) {
136 if (entry->parent->entry[i] == entry) {
137 entry->parent->entry[i] = NULL;
148 /* Finds first occurence of entry named `name' under the directory `dir'.
149 This does not check subdirectories recursively. */
151 static MemFSEntry mem_find_entry(MemFSEntry dir, const char *name,
156 for (i = 0; i < dir->entry_count; i++) {
160 if (!strncmp(name, dir->entry[i]->name, name_len))
161 return dir->entry[i];
167 /* Finds the entry by the `path' which may include full path or
170 static MemFSEntry mem_find_entry_path(MemFSEntry dir, const char *p)
172 MemFSEntry entry = NULL;
176 cp = path = mem_expand_path(dir, p);
178 if (strlen(cp) == 1 && cp[0] == '/')
183 len = strcspn(cp, DIR_SEPARATOR);
185 entry = mem_find_entry(dir, cp, len);
194 len = strcspn(cp, DIR_SEPARATOR);
202 /* Deletes entry by the name `name' from the directory `dir'. This does
203 not check subdirectories recursively. */
205 static bool mem_del_entry_name(MemFSEntry dir, const char *name,
206 uint32 name_len, bool check_perm)
210 /* Files cannot be removed from remote access */
214 entry = mem_find_entry(dir, name, name_len);
217 return mem_del_entry(entry, check_perm);
222 /* Create new handle and add it to the list of open handles. */
224 static MemFSFileHandle mem_create_handle(MemFS fs, int fd, MemFSEntry entry)
226 MemFSFileHandle handle;
229 handle = silc_calloc(1, sizeof(*handle));
231 handle->entry = entry;
234 fs->handles = silc_calloc(5, sizeof(*fs->handles));
235 fs->handles[0] = handle;
236 fs->handles_count = 5;
243 for (i = 0; i < fs->handles_count; i++) {
247 fs->handles[i] = handle;
254 fs->handles = silc_realloc(fs->handles, sizeof(*fs->handles) *
255 (fs->handles_count + 5));
256 for (i = fs->handles_count + 1; i < fs->handles_count + 5; i++)
257 fs->handles[i] = NULL;
258 fs->handles[fs->handles_count] = handle;
259 handle->handle = fs->handles_count;
260 fs->handles_count += 5;
265 /* Deletes the handle and remove it from the open handle list. */
267 static bool mem_del_handle(MemFS fs, MemFSFileHandle handle)
269 if (handle->handle > fs->handles_count)
272 if (!fs->handles[handle->handle])
275 if (fs->handles[handle->handle] == handle) {
276 fs->handles[handle->handle] = NULL;
277 if (handle->fd != -1)
286 /* Find handle by handle index. */
288 static MemFSFileHandle mem_find_handle(MemFS fs, uint32 handle)
290 if (handle > fs->handles_count)
293 if (!fs->handles[handle])
296 if (fs->handles[handle]->handle != handle)
299 return fs->handles[handle];
302 /* Allocates memory filesystem context and returns the context. The
303 context can be given as argument to the silc_sftp_server_start function.
304 The context must be freed by the caller using the function
305 silc_sftp_fs_memory_free. The `perm' is the permissions for the root
306 directory of the filesystem (/ dir). */
308 void *silc_sftp_fs_memory_alloc(SilcSFTPFSMemoryPerm perm)
312 fs = silc_calloc(1, sizeof(*fs));
313 fs->root = silc_calloc(1, sizeof(*fs->root));
314 fs->root->perm = perm;
315 fs->root_perm = perm;
316 fs->root->directory = TRUE;
317 fs->root->name = strdup(DIR_SEPARATOR);
322 /* Frees the memory filesystem context. */
324 void silc_sftp_fs_memory_free(void *context)
326 MemFS fs = (MemFS)context;
332 /* Adds a new directory to the memory filesystem. Returns the directory
333 context that can be used to add for example files to the directory
334 or new subdirectories under the directory. The `dir' is the parent
335 directory of the directory to be added. If this directory is to be
336 added to the root directory the `dir' is NULL. The `name' is the name
337 of the directory. If error occurs this returns NULL. The caller must
338 not free the returned context. The `perm' will indicate the permissions
339 for the directory and they work in POSIX style. */
341 void *silc_sftp_fs_memory_add_dir(void *context, void *dir,
342 SilcSFTPFSMemoryPerm perm,
345 MemFS fs = (MemFS)context;
348 entry = silc_calloc(1, sizeof(*entry));
350 entry->name = strdup(name);
351 entry->directory = TRUE;
352 entry->parent = dir ? dir : fs->root;
354 if (!mem_add_entry(dir ? dir : fs->root, entry, FALSE))
360 /* Deletes a directory indicated by the `dir'. All files and subdirectories
361 in this directory is also removed. If the `dir' is NULL then all
362 directories and files are removed from the filesystem. Returns TRUE
363 if the removing was success. This is the only way to remove directories
364 in memory file system. The filesystem does not allow removing directories
365 with remote access using the filesystem access function sftp_rmdir. */
367 bool silc_sftp_fs_memory_del_dir(void *context, void *dir)
369 MemFS fs = (MemFS)context;
373 return mem_del_entry(dir, FALSE);
375 /* Remove from root */
376 ret = mem_del_entry(fs->root, FALSE);
378 fs->root = silc_calloc(1, sizeof(*fs->root));
379 fs->root->perm = fs->root_perm;
380 fs->root->directory = TRUE;
381 fs->root->name = strdup(DIR_SEPARATOR);
386 /* Adds a new file to the directory indicated by the `dir'. If the `dir'
387 is NULL the file is added to the root directory. The `filename' is the
388 filename in the directory. The `realpath' is the real filepath in the
389 physical filesystem. It is used to actually access the file from the
390 memory filesystem. The `perm' will indicate the permissions for th e
391 file and they work in POSIX style. Returns TRUE if the file was
392 added to the directory. */
394 bool silc_sftp_fs_memory_add_file(void *context,
396 SilcSFTPFSMemoryPerm perm,
397 const char *filename,
398 const char *realpath)
400 MemFS fs = (MemFS)context;
403 entry = silc_calloc(1, sizeof(*entry));
405 entry->name = strdup(filename);
406 entry->data = strdup(realpath);
407 entry->directory = FALSE;
409 return mem_add_entry(dir ? dir : fs->root, entry, FALSE);
412 /* Removes a file indicated by the `filename' from the directory
413 indicated by the `dir'. Returns TRUE if the removing was success. */
415 bool silc_sftp_fs_memory_del_file(void *context, void *dir,
416 const char *filename)
418 MemFS fs = (MemFS)context;
423 return mem_del_entry_name(dir ? dir : fs->root, filename,
424 strlen(filename), FALSE);
427 SilcSFTPHandle mem_get_handle(void *context, SilcSFTP sftp,
428 const unsigned char *data,
431 MemFS fs = (MemFS)context;
437 SILC_GET32_MSB(handle, data);
438 return (SilcSFTPHandle)mem_find_handle(fs, handle);
441 unsigned char *mem_encode_handle(void *context, SilcSFTP sftp,
442 SilcSFTPHandle handle,
446 MemFSFileHandle h = (MemFSFileHandle)handle;
448 data = silc_calloc(4, sizeof(*data));
449 SILC_PUT32_MSB(h->handle, data);
455 void mem_open(void *context, SilcSFTP sftp,
456 const char *filename,
457 SilcSFTPFileOperation pflags,
458 SilcSFTPAttributes attrs,
459 SilcSFTPHandleCallback callback,
460 void *callback_context)
462 MemFS fs = (MemFS)context;
464 MemFSFileHandle handle;
467 /* CREAT and TRUNC not supported */
468 if ((pflags & SILC_SFTP_FXF_CREAT) || (pflags & SILC_SFTP_FXF_TRUNC)) {
469 (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, callback_context);
474 entry = mem_find_entry_path(fs->root, filename);
476 (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
480 if (entry->directory || !entry->data) {
481 (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
485 /* Check for reading */
486 if ((pflags & SILC_SFTP_FXF_READ) &&
487 !(entry->perm & SILC_SFTP_FS_PERM_READ)) {
488 (*callback)(sftp, SILC_SFTP_STATUS_PERMISSION_DENIED, NULL,
493 /* Check for writing */
494 if (((pflags & SILC_SFTP_FXF_WRITE) || (pflags & SILC_SFTP_FXF_APPEND)) &&
495 !(entry->perm & SILC_SFTP_FS_PERM_WRITE)) {
496 (*callback)(sftp, SILC_SFTP_STATUS_PERMISSION_DENIED, NULL,
501 if ((pflags & SILC_SFTP_FXF_READ) && (pflags & SILC_SFTP_FXF_WRITE))
503 else if (pflags & SILC_SFTP_FXF_READ)
505 else if (pflags & SILC_SFTP_FXF_WRITE)
507 if (pflags & SILC_SFTP_FXF_APPEND)
510 /* Attempt to open the file for real. */
511 fd = open(entry->data + 7, flags,
512 (attrs->flags & SILC_SFTP_ATTR_PERMISSIONS ?
513 attrs->permissions : 0600));
515 (*callback)(sftp, silc_sftp_map_errno(errno), NULL, callback_context);
519 /* File opened, return handle */
520 handle = mem_create_handle(fs, fd, entry);
521 (*callback)(sftp, SILC_SFTP_STATUS_OK, (SilcSFTPHandle)handle,
525 void mem_close(void *context, SilcSFTP sftp,
526 SilcSFTPHandle handle,
527 SilcSFTPStatusCallback callback,
528 void *callback_context)
530 MemFS fs = (MemFS)context;
531 MemFSFileHandle h = (MemFSFileHandle)handle;
537 (*callback)(sftp, silc_sftp_map_errno(errno), NULL, NULL,
543 mem_del_handle(fs, h);
544 (*callback)(sftp, SILC_SFTP_STATUS_OK, NULL, NULL, callback_context);
547 void mem_read(void *context, SilcSFTP sftp,
548 SilcSFTPHandle handle,
551 SilcSFTPDataCallback callback,
552 void *callback_context)
554 MemFSFileHandle h = (MemFSFileHandle)handle;
561 data = silc_malloc(len);
562 lseek(h->fd, (off_t)offset, SEEK_SET);
564 /* Attempt to read */
565 ret = read(h->fd, data, len);
568 (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, 0, callback_context);
570 (*callback)(sftp, silc_sftp_map_errno(errno), NULL, 0, callback_context);
576 (*callback)(sftp, SILC_SFTP_STATUS_OK, (const unsigned char *)data,
577 ret, callback_context);
582 void mem_write(void *context, SilcSFTP sftp,
583 SilcSFTPHandle handle,
585 const unsigned char *data,
587 SilcSFTPStatusCallback callback,
588 void *callback_context)
590 MemFSFileHandle h = (MemFSFileHandle)handle;
593 lseek(h->fd, (off_t)offset, SEEK_SET);
595 /* Attempt to write */
596 ret = write(h->fd, data, data_len);
598 (*callback)(sftp, silc_sftp_map_errno(errno), NULL, NULL,
603 (*callback)(sftp, SILC_SFTP_STATUS_OK, NULL, NULL, callback_context);
606 void mem_remove(void *context, SilcSFTP sftp,
607 const char *filename,
608 SilcSFTPStatusCallback callback,
609 void *callback_context)
611 /* Remove is not supported */
612 (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL,
616 void mem_rename(void *context, SilcSFTP sftp,
619 SilcSFTPStatusCallback callback,
620 void *callback_context)
622 /* Rename is not supported */
623 (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL,
627 void mem_mkdir(void *context, SilcSFTP sftp,
629 SilcSFTPAttributes attrs,
630 SilcSFTPStatusCallback callback,
631 void *callback_context)
633 /* Mkdir is not supported */
634 (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL,
638 void mem_rmdir(void *context, SilcSFTP sftp,
640 SilcSFTPStatusCallback callback,
641 void *callback_context)
643 /* Rmdir is not supported */
644 (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL,
648 void mem_opendir(void *context, SilcSFTP sftp,
650 SilcSFTPHandleCallback callback,
651 void *callback_context)
653 MemFS fs = (MemFS)context;
655 MemFSFileHandle handle;
657 if (!path || !strlen(path))
658 path = (const char *)strdup("/");
660 /* Find such directory */
661 entry = mem_find_entry_path(fs->root, path);
663 (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
667 if (!entry->directory) {
668 (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
672 /* Must be read permissions to open a directory */
673 if (!(entry->perm & SILC_SFTP_FS_PERM_READ)) {
674 (*callback)(sftp, SILC_SFTP_STATUS_PERMISSION_DENIED, NULL,
679 /* Directory opened, return handle */
680 handle = mem_create_handle(fs, 0, entry);
681 (*callback)(sftp, SILC_SFTP_STATUS_OK, (SilcSFTPHandle)handle,
685 void mem_readdir(void *context, SilcSFTP sftp,
686 SilcSFTPHandle handle,
687 SilcSFTPNameCallback callback,
688 void *callback_context)
690 MemFSFileHandle h = (MemFSFileHandle)handle;
693 SilcSFTPAttributes attrs;
696 unsigned long filesize = 0;
700 if (!h->entry->directory) {
701 (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
706 (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, callback_context);
710 name = silc_calloc(1, sizeof(*name));
711 for (i = h->fd; i < 100 + h->fd; i++) {
712 if (i >= h->entry->entry_count)
715 entry = h->entry->entry[i];
719 filesize = sizeof(*entry);
720 memset(long_name, 0, sizeof(long_name));
722 date = ctime(&entry->created);
723 if (strrchr(date, ':'))
724 *strrchr(date, ':') = '\0';
726 if (!entry->directory)
727 if (!lstat(entry->data + 7, &stats))
728 filesize = stats.st_size;
730 /* Long name format is:
731 drwx------ 1 324210 Apr 8 08:40 mail/
732 1234567890 123 12345678 123456789012 */
733 snprintf(long_name, sizeof(long_name),
734 "%c%c%c%c------ %3d %8lu %12s %s%s",
735 (entry->directory ? 'd' : '-'),
736 ((entry->perm & SILC_SFTP_FS_PERM_READ) ? 'r' : '-'),
737 ((entry->perm & SILC_SFTP_FS_PERM_WRITE) ? 'w' : '-'),
738 ((entry->perm & SILC_SFTP_FS_PERM_EXEC) ? 'x' : '-'),
739 (entry->directory ? (int)entry->entry_count : 1),
740 filesize, date, entry->name,
741 (entry->directory ? "/" :
742 ((entry->perm & SILC_SFTP_FS_PERM_EXEC) ? "*" : "")));
745 attrs = silc_calloc(1, sizeof(*attrs));
746 attrs->flags = (SILC_SFTP_ATTR_SIZE |
747 SILC_SFTP_ATTR_UIDGID);
748 attrs->size = filesize;
749 attrs->uid = 0; /* We use always 0 UID and GID */
751 if (!entry->directory) {
752 attrs->flags |= SILC_SFTP_ATTR_ACMODTIME;
753 attrs->atime = stats.st_atime;
754 attrs->mtime = stats.st_mtime;
758 silc_sftp_name_add(name, entry->name, long_name, attrs);
761 /* If we didn't read all then udpate the index for next read */
762 if (i >= h->entry->entry_count)
767 /* If names was not found then return EOF. */
768 if (name->count == 0) {
769 (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, callback_context);
770 silc_sftp_name_free(name);
775 (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPName)name,
778 silc_sftp_name_free(name);
781 void mem_stat(void *context, SilcSFTP sftp,
783 SilcSFTPAttrCallback callback,
784 void *callback_context)
786 MemFS fs = (MemFS)context;
788 SilcSFTPAttributes attrs;
792 if (!path || !strlen(path))
793 path = (const char *)strdup("/");
795 /* Find such directory */
796 entry = mem_find_entry_path(fs->root, path);
798 (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
802 if (entry->directory || !entry->data) {
803 (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
808 ret = stat(entry->data + 7, &stats);
810 (*callback)(sftp, silc_sftp_map_errno(errno), NULL, callback_context);
814 attrs = silc_calloc(1, sizeof(*attrs));
815 attrs->flags = (SILC_SFTP_ATTR_SIZE |
816 SILC_SFTP_ATTR_UIDGID |
817 SILC_SFTP_ATTR_ACMODTIME);
818 attrs->size = stats.st_size;
819 attrs->uid = 0; /* We use always 0 UID and GID */
821 attrs->atime = stats.st_atime;
822 attrs->mtime = stats.st_mtime;
824 /* Return attributes */
825 (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPAttributes)attrs,
828 silc_sftp_attr_free(attrs);
831 void mem_lstat(void *context, SilcSFTP sftp,
833 SilcSFTPAttrCallback callback,
834 void *callback_context)
836 MemFS fs = (MemFS)context;
838 SilcSFTPAttributes attrs;
842 if (!path || !strlen(path))
843 path = (const char *)strdup("/");
845 /* Find such directory */
846 entry = mem_find_entry_path(fs->root, path);
848 (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
852 if (entry->directory || !entry->data) {
853 (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
858 ret = lstat(entry->data + 7, &stats);
860 (*callback)(sftp, silc_sftp_map_errno(errno), NULL, callback_context);
864 attrs = silc_calloc(1, sizeof(*attrs));
865 attrs->flags = (SILC_SFTP_ATTR_SIZE |
866 SILC_SFTP_ATTR_UIDGID |
867 SILC_SFTP_ATTR_ACMODTIME);
868 attrs->size = stats.st_size;
869 attrs->uid = 0; /* We use always 0 UID and GID */
871 attrs->atime = stats.st_atime;
872 attrs->mtime = stats.st_mtime;
874 /* Return attributes */
875 (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPAttributes)attrs,
878 silc_sftp_attr_free(attrs);
881 void mem_fstat(void *context, SilcSFTP sftp,
882 SilcSFTPHandle handle,
883 SilcSFTPAttrCallback callback,
884 void *callback_context)
886 MemFSFileHandle h = (MemFSFileHandle)handle;
887 SilcSFTPAttributes attrs;
891 if (h->entry->directory || !h->entry->data) {
892 (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
897 ret = fstat(h->fd, &stats);
899 (*callback)(sftp, silc_sftp_map_errno(errno), NULL, callback_context);
903 attrs = silc_calloc(1, sizeof(*attrs));
904 attrs->flags = (SILC_SFTP_ATTR_SIZE |
905 SILC_SFTP_ATTR_UIDGID |
906 SILC_SFTP_ATTR_ACMODTIME);
907 attrs->size = stats.st_size;
908 attrs->uid = 0; /* We use always 0 UID and GID */
910 attrs->atime = stats.st_atime;
911 attrs->mtime = stats.st_mtime;
913 /* Return attributes */
914 (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPAttributes)attrs,
917 silc_sftp_attr_free(attrs);
920 void mem_setstat(void *context, SilcSFTP sftp,
922 SilcSFTPAttributes attrs,
923 SilcSFTPStatusCallback callback,
924 void *callback_context)
926 /* Setstat is not supported */
927 (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL,
931 void mem_fsetstat(void *context, SilcSFTP sftp,
932 SilcSFTPHandle handle,
933 SilcSFTPAttributes attrs,
934 SilcSFTPStatusCallback callback,
935 void *callback_context)
937 /* Fsetstat is not supported */
938 (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL,
942 void mem_readlink(void *context, SilcSFTP sftp,
944 SilcSFTPNameCallback callback,
945 void *callback_context)
947 /* Readlink is not supported */
948 (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL,
952 void mem_symlink(void *context, SilcSFTP sftp,
953 const char *linkpath,
954 const char *targetpath,
955 SilcSFTPStatusCallback callback,
956 void *callback_context)
958 /* Symlink is not supported */
959 (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL,
963 void mem_realpath(void *context, SilcSFTP sftp,
965 SilcSFTPNameCallback callback,
966 void *callback_context)
968 MemFS fs = (MemFS)context;
972 if (!path || !strlen(path))
973 path = (const char *)strdup("/");
975 realpath = mem_expand_path(fs->root, path);
977 (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
981 name = silc_calloc(1, sizeof(*name));
982 name->filename = silc_calloc(1, sizeof(*name->filename));
983 name->filename[0] = realpath;
984 name->long_filename = silc_calloc(1, sizeof(*name->long_filename));
985 name->long_filename[0] = realpath;
986 name->attrs = silc_calloc(1, sizeof(*name->attrs));
987 name->attrs[0] = silc_calloc(1, sizeof(*name->attrs[0]));
990 (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, (const SilcSFTPName)name,
993 silc_sftp_name_free(name);
996 void mem_extended(void *context, SilcSFTP sftp,
998 const unsigned char *data,
1000 SilcSFTPExtendedCallback callback,
1001 void *callback_context)
1003 /* Extended is not supported */
1004 (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, 0,
1008 struct SilcSFTPFilesystemStruct silc_sftp_fs_memory = {