From 4e56efafe65b56ae474a09befcdd6aef7fdae87d Mon Sep 17 00:00:00 2001 From: Slava Zanko Date: Fri, 24 Jun 2011 21:09:12 +0300 Subject: [PATCH] Ticket #81: savannah: can't access files on ftp starting with space files starting with space can't be acessed by mc through ftp - it shows the file as not having the leading space and attempts to access it produce 'permission denied' errors. Signed-off-by: Slava Zanko --- lib/tests/vfs/Makefile.am | 4 + lib/tests/vfs/vfs_parse_ls_lga.c | 316 +++++++++++++++++++++++++++++++ lib/vfs/direntry.c | 25 +++ lib/vfs/utilvfs.c | 19 +- lib/vfs/utilvfs.h | 2 +- lib/vfs/xdirentry.h | 12 +- src/vfs/extfs/extfs.c | 3 +- src/vfs/ftpfs/ftpfs.c | 6 +- 8 files changed, 377 insertions(+), 10 deletions(-) create mode 100644 lib/tests/vfs/vfs_parse_ls_lga.c diff --git a/lib/tests/vfs/Makefile.am b/lib/tests/vfs/Makefile.am index 93ec93156..aeaa82594 100644 --- a/lib/tests/vfs/Makefile.am +++ b/lib/tests/vfs/Makefile.am @@ -10,6 +10,7 @@ TESTS = \ canonicalize_pathname \ current_dir \ path_serialize \ + vfs_parse_ls_lga \ vfs_path_string_convert \ vfs_prefix_to_class \ vfs_split \ @@ -29,6 +30,9 @@ path_serialize_SOURCES = \ vfs_split_SOURCES = \ vfs_split.c +vfs_parse_ls_lga_SOURCES = \ + vfs_parse_ls_lga.c + vfs_prefix_to_class_SOURCES = \ vfs_prefix_to_class.c diff --git a/lib/tests/vfs/vfs_parse_ls_lga.c b/lib/tests/vfs/vfs_parse_ls_lga.c new file mode 100644 index 000000000..b8d852afa --- /dev/null +++ b/lib/tests/vfs/vfs_parse_ls_lga.c @@ -0,0 +1,316 @@ +/* lib/vfs - test vfs_parse_ls_lga() functionality + + Copyright (C) 2011 Free Software Foundation, Inc. + + Written by: + Slava Zanko , 2011 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License + as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#define TEST_SUITE_NAME "/lib/vfs" + +#include +#include + + +#include "lib/global.h" +#include "lib/vfs/utilvfs.h" +#include "lib/vfs/xdirentry.h" +#include "lib/strutil.h" + +#include "src/vfs/local/local.c" + + +struct vfs_s_subclass test_subclass1; +struct vfs_class vfs_test_ops1; + +struct vfs_s_entry *vfs_root_entry; +static struct vfs_s_inode *vfs_root_inode; +static struct vfs_s_super *vfs_test_super; + +void message (int flags, const char *title, const char *text, ...); + + +static void +setup (void) +{ + static struct stat initstat; + + str_init_strings (NULL); + + vfs_init (); + init_localfs (); + vfs_setup_work_dir (); + + test_subclass1.flags = VFS_S_REMOTE; + vfs_s_init_class (&vfs_test_ops1, &test_subclass1); + vfs_test_ops1.name = "testfs1"; + vfs_test_ops1.flags = VFSF_NOLINKS; + vfs_test_ops1.prefix = "test1:"; + vfs_register_class (&vfs_test_ops1); + + vfs_test_super = g_new0 (struct vfs_s_super, 1); + vfs_test_super->me = &vfs_test_ops1; + + vfs_root_inode = vfs_s_new_inode (&vfs_test_ops1, vfs_test_super, &initstat); + vfs_root_entry = vfs_s_new_entry (&vfs_test_ops1, "/", vfs_root_inode); +} + +static void +teardown (void) +{ + vfs_s_free_entry (&vfs_test_ops1, vfs_root_entry); + vfs_shut (); + str_uninit_strings (); +} + + +void +message (int flags, const char *title, const char *text, ...) +{ + char *p; + va_list ap; + + (void) flags; + (void) title; + + va_start (ap, text); + p = g_strdup_vprintf (text, ap); + va_end (ap); + printf("message(): %s\n", p); + g_free(p); +} + +/* --------------------------------------------------------------------------------------------- */ + +#define check_one_stat_field(etalon_stat, test_stat, field, format)\ +{\ + fail_unless(etalon_stat.field == test_stat.field,\ + "\netalon."#field" = " format "\nactual."#field" = " format "\n",\ + etalon_stat.field, test_stat.field);\ +} + +#define check_stat_struct(etalon_stat, test_stat)\ +{\ + check_one_stat_field(etalon_stat, test_stat, st_dev, "%zu");\ + check_one_stat_field(etalon_stat, test_stat, st_ino, "%zu");\ + check_one_stat_field(etalon_stat, test_stat, st_ino, "%zu");\ + check_one_stat_field(etalon_stat, test_stat, st_mode, "%04x");\ + check_one_stat_field(etalon_stat, test_stat, st_uid, "%u");\ + check_one_stat_field(etalon_stat, test_stat, st_gid, "%u");\ + check_one_stat_field(etalon_stat, test_stat, st_rdev, "%zu");\ + check_one_stat_field(etalon_stat, test_stat, st_size, "%zd");\ + check_one_stat_field(etalon_stat, test_stat, st_blksize, "%zu");\ + check_one_stat_field(etalon_stat, test_stat, st_blocks, "%zd");\ + check_one_stat_field(etalon_stat, test_stat, st_atime, "%zd");\ + check_one_stat_field(etalon_stat, test_stat, st_mtime, "%zd");\ + check_one_stat_field(etalon_stat, test_stat, st_ctime, "%zd");\ +} + +static void check_vfs_parse_ls_lga_call(const char *input_data, int etalon_result, +const char *etalon_filename, const char *etalon_linkname, struct stat etalon_stat, size_t *filepos) +{ + static struct stat test_stat; + char *filename = NULL; + char *linkname = NULL; + gboolean result; + + result = vfs_parse_ls_lga (input_data, &test_stat, &filename, &linkname, filepos); + + fail_if (result != etalon_result, + "\nactual result: %d\netalon result: %d\n", result, etalon_result); + + fail_unless((filename != NULL && etalon_filename != NULL && strcmp(filename, etalon_filename) == 0) + || (filename == NULL && etalon_filename == filename), + "\nactual filename '%s'\netalon filename '%s'", filename, etalon_filename); + + fail_unless((linkname != NULL && etalon_linkname != NULL && strcmp(linkname, etalon_linkname) == 0) + || (linkname == NULL && etalon_linkname == linkname), + "\nactual linkname '%s'\netalon linkname '%s'", linkname, etalon_linkname); + + check_stat_struct(etalon_stat, test_stat); + +} + +START_TEST (test_vfs_parse_ls_lga) +{ + size_t filepos = 0; + + check_vfs_parse_ls_lga_call( + "drwxrwxr-x 10 500 500 4096 Jun 23 17:09 build_root", + 1, "build_root", NULL, (struct stat) + { + .st_dev = 0, + .st_ino = 0, + .st_mode = 0x41fd, + .st_nlink = 10, + .st_uid = 500, + .st_gid = 500, + .st_rdev = 0, + .st_size = 4096, + .st_blksize = 512, + .st_blocks = 8, + .st_atime = 1308838140, + .st_mtime = 1308838140, + .st_ctime = 1308838140 + }, + NULL + ); + + check_vfs_parse_ls_lga_call( + "lrwxrwxrwx 1 500 500 11 Mar 13 2010 COPYING -> doc/COPYING", + 1, "COPYING", "doc/COPYING", + (struct stat) + { + .st_dev = 0, + .st_ino = 0, + .st_mode = 0xa1ff, + .st_nlink = 10, + .st_uid = 500, + .st_gid = 500, + .st_rdev = 0, + .st_size = 11, + .st_blksize = 512, + .st_blocks = 1, + .st_atime = 1268431200, + .st_mtime = 1268431200, + .st_ctime = 1268431200 + }, + NULL + ); + + check_vfs_parse_ls_lga_call( + "drwxrwxr-x 10 500 500 4096 Jun 23 17:09 ..", + 1, "..", NULL, (struct stat) + { + .st_dev = 0, + .st_ino = 0, + .st_mode = 0x41fd, + .st_nlink = 10, + .st_uid = 500, + .st_gid = 500, + .st_rdev = 0, + .st_size = 4096, + .st_blksize = 512, + .st_blocks = 8, + .st_atime = 1308838140, + .st_mtime = 1308838140, + .st_ctime = 1308838140 + }, + &filepos + ); + + check_vfs_parse_ls_lga_call( + "drwxrwxr-x 10 500 500 4096 Jun 23 17:09 build_root", + 1, " build_root", NULL, (struct stat) + { + .st_dev = 0, + .st_ino = 0, + .st_mode = 0x41fd, + .st_nlink = 10, + .st_uid = 500, + .st_gid = 500, + .st_rdev = 0, + .st_size = 4096, + .st_blksize = 512, + .st_blocks = 8, + .st_atime = 1308838140, + .st_mtime = 1308838140, + .st_ctime = 1308838140 + }, + &filepos + ); + +} +END_TEST + +/* --------------------------------------------------------------------------------------------- */ + +START_TEST (test_vfs_parse_ls_lga_reorder) +{ + size_t filepos = 0; + struct vfs_s_entry *ent1, *ent2, *ent3; + int i; + + + ent1 = vfs_s_generate_entry (&vfs_test_ops1, NULL, vfs_root_inode, 0); + i = ent1->ino->st.st_nlink; + if (! vfs_parse_ls_lga ("drwxrwxr-x 10 500 500 4096 Jun 23 17:09 build_root1", + &ent1->ino->st, &ent1->name, &ent1->ino->linkname, &filepos)) + { + fail ("An error occured while parse ls output"); + return; + } + vfs_s_store_filename_pos (ent1, filepos); + vfs_s_insert_entry (&vfs_test_ops1, vfs_root_inode, ent1); + + + ent2 = vfs_s_generate_entry (&vfs_test_ops1, NULL, vfs_root_inode, 0); + i = ent2->ino->st.st_nlink; + if (! vfs_parse_ls_lga ("drwxrwxr-x 10 500 500 4096 Jun 23 17:09 build_root2", + &ent2->ino->st, &ent2->name, &ent2->ino->linkname, &filepos)) + { + fail ("An error occured while parse ls output"); + return; + } + vfs_s_store_filename_pos (ent2, filepos); + vfs_s_insert_entry (&vfs_test_ops1, vfs_root_inode, ent2); + + ent3 = vfs_s_generate_entry (&vfs_test_ops1, NULL, vfs_root_inode, 0); + i = ent3->ino->st.st_nlink; + if (! vfs_parse_ls_lga ("drwxrwxr-x 10 500 500 4096 Jun 23 17:09 ..", + &ent3->ino->st, &ent3->name, &ent3->ino->linkname, &filepos)) + { + fail ("An error occured while parse ls output"); + return; + } + vfs_s_store_filename_pos (ent3, filepos); + vfs_s_insert_entry (&vfs_test_ops1, vfs_root_inode, ent3); + + vfs_s_normalize_filename_pos (vfs_root_inode, filepos); + + fail_unless(strcmp(ent1->name, " build_root1") == 0, "\nactual '%s'\nnot equal to '%s'\n", ent1->name, " build_root1"); + fail_unless(strcmp(ent2->name, " build_root2") == 0, "\nactual '%s'\nnot equal to '%s'\n", ent2->name, " build_root2"); +} +END_TEST + +/* --------------------------------------------------------------------------------------------- */ +int +main (void) +{ + int number_failed; + + Suite *s = suite_create (TEST_SUITE_NAME); + TCase *tc_core = tcase_create ("Core"); + SRunner *sr; + + tcase_add_checked_fixture (tc_core, setup, teardown); + + /* Add new tests here: *************** */ + tcase_add_test (tc_core, test_vfs_parse_ls_lga); + tcase_add_test (tc_core, test_vfs_parse_ls_lga_reorder); + /* *********************************** */ + + suite_add_tcase (s, tc_core); + sr = srunner_create (s); + srunner_set_log (sr, "vfs_parse_ls_lga.log"); + srunner_run_all (sr, CK_NORMAL); + number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? 0 : 1; +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/lib/vfs/direntry.c b/lib/vfs/direntry.c index b2bc12b45..8b6bbf63b 100644 --- a/lib/vfs/direntry.c +++ b/lib/vfs/direntry.c @@ -1500,3 +1500,28 @@ vfs_s_get_line_interruptible (struct vfs_class *me, char *buffer, int size, int #endif /* ENABLE_VFS_NET */ /* --------------------------------------------------------------------------------------------- */ +/** + * Normalize filenames start position + */ + +void +vfs_s_normalize_filename_pos (struct vfs_s_inode *root_inode, size_t final_filepos) +{ + GList *iter; + + for (iter = root_inode->subdir; iter != NULL; iter = g_list_next (iter)) + { + struct vfs_s_entry *entry = (struct vfs_s_entry *) iter->data; + if ((size_t) entry->ino->data_offset > final_filepos) + { + char *source_name = entry->name; + char *spacer = g_strnfill (entry->ino->data_offset - final_filepos, ' '); + entry->name = g_strdup_printf ("%s%s", spacer, source_name); + g_free (spacer); + g_free (source_name); + } + entry->ino->data_offset = -1; + } +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/lib/vfs/utilvfs.c b/lib/vfs/utilvfs.c index b56297f17..b7bfb2309 100644 --- a/lib/vfs/utilvfs.c +++ b/lib/vfs/utilvfs.c @@ -918,8 +918,9 @@ vfs_parse_filedate (int idx, time_t * t) /* --------------------------------------------------------------------------------------------- */ -int -vfs_parse_ls_lga (const char *p, struct stat *s, char **filename, char **linkname) +gboolean +vfs_parse_ls_lga (const char *p, struct stat * s, char **filename, char **linkname, + size_t * filename_pos) { int idx, idx2, num_cols; int i; @@ -929,7 +930,7 @@ vfs_parse_ls_lga (const char *p, struct stat *s, char **filename, char **linknam size_t skipped; if (strncmp (p, "total", 5) == 0) - return 0; + return FALSE; if (!vfs_parse_filetype (p, &skipped, &s->st_mode)) goto error; @@ -1047,6 +1048,14 @@ vfs_parse_ls_lga (const char *p, struct stat *s, char **filename, char **linknam s->st_blocks = (s->st_size + 511) / 512; #endif + if (filename_pos != NULL) + { + if ((*filename_pos == 0) || (*filename_pos > (size_t) column_ptr[idx])) + *filename_pos = column_ptr[idx]; + else + column_ptr[idx] = *filename_pos; + } + for (i = idx + 1, idx2 = 0; i < num_cols; i++) if (strcmp (columns[i], "->") == 0) { @@ -1096,7 +1105,7 @@ vfs_parse_ls_lga (const char *p, struct stat *s, char **filename, char **linknam } g_free (p_copy); - return 1; + return TRUE; error: { @@ -1111,7 +1120,7 @@ vfs_parse_ls_lga (const char *p, struct stat *s, char **filename, char **linknam } g_free (p_copy); - return 0; + return FALSE; } /* --------------------------------------------------------------------------------------------- */ diff --git a/lib/vfs/utilvfs.h b/lib/vfs/utilvfs.h index 990a972b8..0ac47ef0d 100644 --- a/lib/vfs/utilvfs.h +++ b/lib/vfs/utilvfs.h @@ -53,7 +53,7 @@ gboolean vfs_parse_fileperms (const char *s, size_t * ret_skipped, mode_t * ret_ gboolean vfs_parse_filemode (const char *s, size_t * ret_skipped, mode_t * ret_mode); gboolean vfs_parse_raw_filemode (const char *s, size_t * ret_skipped, mode_t * ret_mode); -int vfs_parse_ls_lga (const char *p, struct stat *s, char **filename, char **linkname); +gboolean vfs_parse_ls_lga (const char *p, struct stat *s, char **filename, char **linkname, size_t *filename_pos); int vfs_parse_filedate (int idx, time_t * t); /*** inline functions ****************************************************************************/ diff --git a/lib/vfs/xdirentry.h b/lib/vfs/xdirentry.h index 12b4669ec..d02f00152 100644 --- a/lib/vfs/xdirentry.h +++ b/lib/vfs/xdirentry.h @@ -168,8 +168,7 @@ struct vfs_s_inode *vfs_s_find_root (struct vfs_class *me, struct vfs_s_entry *e /* outside interface */ void vfs_s_init_class (struct vfs_class *vclass, struct vfs_s_subclass *sub); -const char *vfs_s_get_path (const vfs_path_t * vpath, struct vfs_s_super **archive, - int flags); +const char *vfs_s_get_path (const vfs_path_t * vpath, struct vfs_s_super **archive, int flags); void vfs_s_invalidate (struct vfs_class *me, struct vfs_s_super *super); char *vfs_s_fullpath (struct vfs_class *me, struct vfs_s_inode *ino); @@ -181,5 +180,14 @@ int vfs_s_get_line_interruptible (struct vfs_class *me, char *buffer, int size, /* misc */ int vfs_s_retrieve_file (struct vfs_class *me, struct vfs_s_inode *ino); +void vfs_s_normalize_filename_pos (struct vfs_s_inode *root_inode, size_t final_filepos); + /*** inline functions ****************************************************************************/ + +static inline void +vfs_s_store_filename_pos (struct vfs_s_entry *entry, size_t position) +{ + entry->ino->data_offset = (off_t) position; +} + #endif diff --git a/src/vfs/extfs/extfs.c b/src/vfs/extfs/extfs.c index 0bc5e14a7..b495f1bd0 100644 --- a/src/vfs/extfs/extfs.c +++ b/src/vfs/extfs/extfs.c @@ -481,6 +481,7 @@ extfs_read_archive (int fstype, const char *name, struct archive **pparc) char *buffer; struct archive *current_archive; char *current_file_name, *current_link_name; + size_t filepos = 0; info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype); @@ -498,7 +499,7 @@ extfs_read_archive (int fstype, const char *name, struct archive **pparc) struct stat hstat; current_link_name = NULL; - if (vfs_parse_ls_lga (buffer, &hstat, ¤t_file_name, ¤t_link_name)) + if (vfs_parse_ls_lga (buffer, &hstat, ¤t_file_name, ¤t_link_name, &filepos)) { struct entry *entry, *pent; struct inode *inode; diff --git a/src/vfs/ftpfs/ftpfs.c b/src/vfs/ftpfs/ftpfs.c index b9b2ed190..1d5ce1b11 100644 --- a/src/vfs/ftpfs/ftpfs.c +++ b/src/vfs/ftpfs/ftpfs.c @@ -1642,6 +1642,7 @@ ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path int sock, num_entries = 0; char lc_buffer[BUF_8K]; int cd_first; + size_t filepos = 0; cd_first = ftpfs_first_cd_then_ls || (SUP->strict == RFC_STRICT) || (strchr (remote_path, ' ') != NULL); @@ -1712,13 +1713,14 @@ ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path ent = vfs_s_generate_entry (me, NULL, dir, 0); i = ent->ino->st.st_nlink; - if (!vfs_parse_ls_lga (lc_buffer, &ent->ino->st, &ent->name, &ent->ino->linkname)) + if (!vfs_parse_ls_lga (lc_buffer, &ent->ino->st, &ent->name, &ent->ino->linkname, &filepos)) { vfs_s_free_entry (me, ent); continue; } ent->ino->st.st_nlink = i; /* Ouch, we need to preserve our counts :-( */ num_entries++; + vfs_s_store_filename_pos (ent, filepos); vfs_s_insert_entry (me, dir, ent); } @@ -1743,6 +1745,8 @@ ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path goto again; } + vfs_s_normalize_filename_pos (dir, filepos); + if (SUP->strict == RFC_AUTODETECT) SUP->strict = RFC_DARING;