From 4c7223e9f201f1f94c5b50e3a22c1d943f1a32df Mon Sep 17 00:00:00 2001 From: Andrew Borodin Date: Mon, 16 Nov 2020 10:09:09 +0300 Subject: [PATCH] Ticket #4145: file names longer than 255 bytes are not supported. Avoid limitation of file name length. (vfs_dirent): redefined to use instead of standard "struct direct" to hold file name of any length. (vfs_class::readdir): return newly allocated vfs_dirent structure. Related changes. Signed-off-by: Andrew Borodin --- lib/vfs/direntry.c | 8 ++--- lib/vfs/interface.c | 27 +++------------ lib/vfs/vfs.c | 66 +++++++++++++++++++++++++++++++++++-- lib/vfs/vfs.h | 25 +++++++++----- lib/widget/input_complete.c | 2 +- src/filemanager/dir.c | 6 ++-- src/filemanager/file.c | 8 ++--- src/filemanager/find.c | 2 +- src/filemanager/treestore.c | 2 +- src/vfs/extfs/extfs.c | 8 ++--- src/vfs/local/local.c | 8 +++-- src/vfs/sftpfs/dir.c | 9 ++--- src/vfs/sftpfs/internal.h | 2 +- src/vfs/sftpfs/vfs_class.c | 6 ++-- src/vfs/smbfs/smbfs.c | 11 ++++--- src/vfs/undelfs/undelfs.c | 14 +++++--- 16 files changed, 130 insertions(+), 74 deletions(-) diff --git a/lib/vfs/direntry.c b/lib/vfs/direntry.c index 87e71a660..949fe04c5 100644 --- a/lib/vfs/direntry.c +++ b/lib/vfs/direntry.c @@ -453,10 +453,10 @@ vfs_s_opendir (const vfs_path_t * vpath) /* --------------------------------------------------------------------------------------------- */ -static void * +static struct vfs_dirent * vfs_s_readdir (void *data) { - static union vfs_dirent dir; + struct vfs_dirent *dir = NULL; struct dirhandle *info = (struct dirhandle *) data; const char *name; @@ -465,13 +465,13 @@ vfs_s_readdir (void *data) name = VFS_ENTRY (info->cur->data)->name; if (name != NULL) - g_strlcpy (dir.dent.d_name, name, MC_MAXPATHLEN); + dir = vfs_dirent_init (NULL, name, 0); else vfs_die ("Null in structure-cannot happen"); info->cur = g_list_next (info->cur); - return (void *) &dir; + return dir; } /* --------------------------------------------------------------------------------------------- */ diff --git a/lib/vfs/interface.c b/lib/vfs/interface.c index ca29653a2..6c4d9fde0 100644 --- a/lib/vfs/interface.c +++ b/lib/vfs/interface.c @@ -62,12 +62,10 @@ /* TODO: move it to separate private .h */ extern GString *vfs_str_buffer; extern vfs_class *current_vfs; -extern struct dirent *mc_readdir_result; +extern struct vfs_dirent *mc_readdir_result; /*** global variables ****************************************************************************/ -struct dirent *mc_readdir_result = NULL; - /*** file scope macro definitions ****************************************************************/ /*** file scope type declarations ****************************************************************/ @@ -458,30 +456,15 @@ mc_opendir (const vfs_path_t * vpath) /* --------------------------------------------------------------------------------------------- */ -struct dirent * +struct vfs_dirent * mc_readdir (DIR * dirp) { int handle; struct vfs_class *vfs; void *fsinfo = NULL; - struct dirent *entry = NULL; + struct vfs_dirent *entry = NULL; vfs_path_element_t *vfs_path_element; - if (mc_readdir_result == NULL) - { - /* We can't just allocate struct dirent as (see man dirent.h) - * struct dirent has VERY nonnaive semantics of allocating - * d_name in it. Moreover, linux's glibc-2.9 allocates dirents _less_, - * than 'sizeof (struct dirent)' making full bitwise (sizeof dirent) copy - * heap corrupter. So, allocate longliving dirent with at least - * (MAXNAMLEN + 1) for d_name in it. - * Strictly saying resulting dirent is unusable as we don't adjust internal - * structures, holding dirent size. But we don't use it in libc infrastructure. - * TODO: to make simpler homemade dirent-alike structure. - */ - mc_readdir_result = (struct dirent *) g_malloc (sizeof (struct dirent) + MAXNAMLEN + 1); - } - if (dirp == NULL) { errno = EFAULT; @@ -507,8 +490,8 @@ mc_readdir (DIR * dirp) #else g_string_assign (vfs_str_buffer, entry->d_name); #endif - mc_readdir_result->d_ino = entry->d_ino; - g_strlcpy (mc_readdir_result->d_name, vfs_str_buffer->str, MAXNAMLEN + 1); + vfs_dirent_assign (mc_readdir_result, vfs_str_buffer->str, entry->d_ino); + vfs_dirent_free (entry); } if (entry == NULL) errno = vfs->readdir ? vfs_ferrno (vfs) : E_NOTSUPP; diff --git a/lib/vfs/vfs.c b/lib/vfs/vfs.c index 0b948846c..b21cd866e 100644 --- a/lib/vfs/vfs.c +++ b/lib/vfs/vfs.c @@ -69,13 +69,14 @@ #include "gc.h" /* TODO: move it to the separate .h */ -extern struct dirent *mc_readdir_result; +extern struct vfs_dirent *mc_readdir_result; extern GPtrArray *vfs__classes_list; extern GString *vfs_str_buffer; extern vfs_class *current_vfs; /*** global variables ****************************************************************************/ +struct vfs_dirent *mc_readdir_result = NULL; GPtrArray *vfs__classes_list = NULL; GString *vfs_str_buffer = NULL; vfs_class *current_vfs = NULL; @@ -469,6 +470,7 @@ vfs_init (void) vfs_str_buffer = g_string_new (""); + mc_readdir_result = vfs_dirent_init (NULL, "", -1); } /* --------------------------------------------------------------------------------------------- */ @@ -518,10 +520,70 @@ vfs_shut (void) vfs_str_buffer = NULL; current_vfs = NULL; vfs_free_handle_list = -1; - MC_PTR_FREE (mc_readdir_result); + vfs_dirent_free (mc_readdir_result); + mc_readdir_result = NULL; } /* --------------------------------------------------------------------------------------------- */ +/** + * Init or create vfs_dirent structure + * + * @d vfs_dirent structure to init. If NULL, new structure is created. + * @fname file name + * @ino file inode number + * + * @return pointer to d if d isn't NULL, or pointer to newly created structure. + */ + +struct vfs_dirent * +vfs_dirent_init (struct vfs_dirent *d, const char *fname, ino_t ino) +{ + struct vfs_dirent *ret = d; + + if (ret == NULL) + ret = g_new0 (struct vfs_dirent, 1); + + if (ret->d_name_str == NULL) + ret->d_name_str = g_string_sized_new (MC_MAXFILENAMELEN); + + vfs_dirent_assign (ret, fname, ino); + + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Assign members of vfs_dirent structure + * + * @d vfs_dirent structure for assignment + * @fname file name + * @ino file inode number + */ + +void +vfs_dirent_assign (struct vfs_dirent *d, const char *fname, ino_t ino) +{ + g_string_assign (d->d_name_str, fname); + d->d_name = d->d_name_str->str; + d->d_ino = ino; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Destroy vfs_dirent structure + * + * @d vfs_dirent structure to destroy. + */ + +void +vfs_dirent_free (struct vfs_dirent *d) +{ + g_string_free (d->d_name_str, TRUE); + g_free (d); +} + +/* --------------------------------------------------------------------------------------------- */ + /** * These ones grab information from the VFS * and handles them to an upper layer diff --git a/lib/vfs/vfs.h b/lib/vfs/vfs.h index a1d4bf949..2b0e6fb4c 100644 --- a/lib/vfs/vfs.h +++ b/lib/vfs/vfs.h @@ -9,7 +9,7 @@ #include #include -#include +#include /* DIR */ #ifdef HAVE_UTIMENSAT #include #elif defined (HAVE_UTIME_H) @@ -20,7 +20,6 @@ #include #include "lib/global.h" -#include "lib/fs.h" /* MC_MAXPATHLEN */ #include "path.h" @@ -172,7 +171,7 @@ typedef struct vfs_class ssize_t (*write) (void *vfs_info, const char *buf, size_t count); void *(*opendir) (const vfs_path_t * vpath); - void *(*readdir) (void *vfs_info); + struct vfs_dirent *(*readdir) (void *vfs_info); int (*closedir) (void *vfs_info); int (*stat) (const vfs_path_t * vpath, struct stat * buf); @@ -211,13 +210,17 @@ typedef struct vfs_class } vfs_class; /* - * This union is used to ensure that there is enough space for the - * filename (d_name) when the dirent structure is created. + * This struct is used instead of standard dirent to hold file name of any length + * (not limited to NAME_MAX). */ -union vfs_dirent +struct vfs_dirent { - struct dirent dent; - char _extra_buffer[offsetof (struct dirent, d_name) + MC_MAXPATHLEN + 1]; + /* private */ + GString *d_name_str; + + /* public */ + ino_t d_ino; + char *d_name; /* Alias of d_name_str->str */ }; /*** global variables defined in .c file *********************************************************/ @@ -278,6 +281,10 @@ void vfs_stamp_path (const vfs_path_t * path); void vfs_release_path (const vfs_path_t * vpath); +struct vfs_dirent *vfs_dirent_init (struct vfs_dirent *d, const char *fname, ino_t ino); +void vfs_dirent_assign (struct vfs_dirent *d, const char *fname, ino_t ino); +void vfs_dirent_free (struct vfs_dirent *d); + void vfs_fill_names (fill_names_f); /* *INDENT-OFF* */ @@ -309,7 +316,7 @@ int mc_readlink (const vfs_path_t * vpath, char *buf, size_t bufsiz); int mc_close (int handle); off_t mc_lseek (int fd, off_t offset, int whence); DIR *mc_opendir (const vfs_path_t * vpath); -struct dirent *mc_readdir (DIR * dirp); +struct vfs_dirent *mc_readdir (DIR * dirp); int mc_closedir (DIR * dir); int mc_stat (const vfs_path_t * vpath, struct stat *buf); int mc_mknod (const vfs_path_t * vpath, mode_t mode, dev_t dev); diff --git a/lib/widget/input_complete.c b/lib/widget/input_complete.c index 59a994d62..c23fe6063 100644 --- a/lib/widget/input_complete.c +++ b/lib/widget/input_complete.c @@ -139,7 +139,7 @@ filename_completion_function (const char *text, int state, input_complete_t flag static vfs_path_t *dirname_vpath = NULL; gboolean isdir = TRUE, isexec = FALSE; - struct dirent *entry = NULL; + struct vfs_dirent *entry = NULL; SHOW_C_CTX ("filename_completion_function"); diff --git a/src/filemanager/dir.c b/src/filemanager/dir.c index 92b616668..e52473d5b 100644 --- a/src/filemanager/dir.c +++ b/src/filemanager/dir.c @@ -145,7 +145,7 @@ clean_sort_keys (dir_list * list, int start, int count) */ static gboolean -handle_dirent (struct dirent *dp, const char *fltr, struct stat *buf1, gboolean * link_to_dir, +handle_dirent (struct vfs_dirent *dp, const char *fltr, struct stat *buf1, gboolean * link_to_dir, gboolean * stale_link) { vfs_path_t *vpath; @@ -624,7 +624,7 @@ dir_list_load (dir_list * list, const vfs_path_t * vpath, GCompareFunc sort, const dir_sort_options_t * sort_op, const char *fltr) { DIR *dirp; - struct dirent *dp; + struct vfs_dirent *dp; struct stat st; file_entry_t *fentry; const char *vpath_str; @@ -697,7 +697,7 @@ dir_list_reload (dir_list * list, const vfs_path_t * vpath, GCompareFunc sort, const dir_sort_options_t * sort_op, const char *fltr) { DIR *dirp; - struct dirent *dp; + struct vfs_dirent *dp; int i; struct stat st; int marked_cnt; diff --git a/src/filemanager/file.c b/src/filemanager/file.c index b9833de1d..a43ebd17e 100644 --- a/src/filemanager/file.c +++ b/src/filemanager/file.c @@ -624,7 +624,7 @@ do_compute_dir_size (const vfs_path_t * dirname_vpath, dirsize_status_msg_t * ds int res; struct stat s; DIR *dir; - struct dirent *dirent; + struct vfs_dirent *dirent; FileProgressStatus ret = FILE_CONT; (*dir_count)++; @@ -1403,7 +1403,7 @@ try_erase_dir (file_op_context_t * ctx, const char *dir) static FileProgressStatus recursive_erase (file_op_total_context_t * tctx, file_op_context_t * ctx, const vfs_path_t * vpath) { - struct dirent *next; + struct vfs_dirent *next; DIR *reading; const char *s; FileProgressStatus return_status = FILE_CONT; @@ -1458,7 +1458,7 @@ static int check_dir_is_empty (const vfs_path_t * vpath) { DIR *dir; - struct dirent *d; + struct vfs_dirent *d; int i = 1; dir = mc_opendir (vpath); @@ -2766,7 +2766,7 @@ FileProgressStatus copy_dir_dir (file_op_total_context_t * tctx, file_op_context_t * ctx, const char *s, const char *d, gboolean toplevel, gboolean move_over, gboolean do_delete, GSList * parent_dirs) { - struct dirent *next; + struct vfs_dirent *next; struct stat dst_stat, src_stat; DIR *reading; FileProgressStatus return_status = FILE_CONT; diff --git a/src/filemanager/find.c b/src/filemanager/find.c index b7506bf7d..7b1c8a1dd 100644 --- a/src/filemanager/find.c +++ b/src/filemanager/find.c @@ -1247,7 +1247,7 @@ find_rotate_dash (const WDialog * h, gboolean show) static int do_search (WDialog * h) { - static struct dirent *dp = NULL; + static struct vfs_dirent *dp = NULL; static DIR *dirp = NULL; static char *directory = NULL; struct stat tmp_stat; diff --git a/src/filemanager/treestore.c b/src/filemanager/treestore.c index 08354b217..aafb523af 100644 --- a/src/filemanager/treestore.c +++ b/src/filemanager/treestore.c @@ -909,7 +909,7 @@ tree_store_rescan (const vfs_path_t * vpath) dirp = mc_opendir (vpath); if (dirp != NULL) { - struct dirent *dp; + struct vfs_dirent *dp; for (dp = mc_readdir (dirp); dp != NULL; dp = mc_readdir (dirp)) if (!DIR_IS_DOT (dp->d_name) && !DIR_IS_DOTDOT (dp->d_name)) diff --git a/src/vfs/extfs/extfs.c b/src/vfs/extfs/extfs.c index 7ddbc7a80..d4042865e 100644 --- a/src/vfs/extfs/extfs.c +++ b/src/vfs/extfs/extfs.c @@ -1047,20 +1047,20 @@ extfs_opendir (const vfs_path_t * vpath) /* --------------------------------------------------------------------------------------------- */ -static void * +static struct vfs_dirent * extfs_readdir (void *data) { - static union vfs_dirent dir; + struct vfs_dirent *dir; GList **info = (GList **) data; if (*info == NULL) return NULL; - g_strlcpy (dir.dent.d_name, VFS_ENTRY ((*info)->data)->name, MC_MAXPATHLEN); + dir = vfs_dirent_init (NULL, VFS_ENTRY ((*info)->data)->name, 0); /* FIXME: inode */ *info = g_list_next (*info); - return (void *) &dir; + return dir; } /* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/local/local.c b/src/vfs/local/local.c index 717868ad4..cc9fd7a0d 100644 --- a/src/vfs/local/local.c +++ b/src/vfs/local/local.c @@ -103,10 +103,14 @@ local_opendir (const vfs_path_t * vpath) /* --------------------------------------------------------------------------------------------- */ -static void * +static struct vfs_dirent * local_readdir (void *data) { - return readdir (*(DIR **) data); + struct dirent *d; + + d = readdir (*(DIR **) data); + + return (d != NULL ? vfs_dirent_init (NULL, d->d_name, d->d_ino) : NULL); } /* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/sftpfs/dir.c b/src/vfs/sftpfs/dir.c index 67b7f29ee..63a397f39 100644 --- a/src/vfs/sftpfs/dir.c +++ b/src/vfs/sftpfs/dir.c @@ -115,13 +115,12 @@ sftpfs_opendir (const vfs_path_t * vpath, GError ** mcerror) * @return information about direntry if success, NULL otherwise */ -void * +struct vfs_dirent * sftpfs_readdir (void *data, GError ** mcerror) { char mem[BUF_MEDIUM]; LIBSSH2_SFTP_ATTRIBUTES attrs; sftpfs_dir_data_t *sftpfs_dir = (sftpfs_dir_data_t *) data; - static union vfs_dirent sftpfs_dirent; int rc; mc_return_val_if_error (mcerror, NULL); @@ -137,11 +136,7 @@ sftpfs_readdir (void *data, GError ** mcerror) } while (rc == LIBSSH2_ERROR_EAGAIN); - if (rc == 0) - return NULL; - - g_strlcpy (sftpfs_dirent.dent.d_name, mem, BUF_MEDIUM); - return &sftpfs_dirent; + return (rc != 0 ? vfs_dirent_init (NULL, mem, 0) : NULL); /* FIXME: inode */ } /* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/sftpfs/internal.h b/src/vfs/sftpfs/internal.h index 84b9d523e..d2398c4ed 100644 --- a/src/vfs/sftpfs/internal.h +++ b/src/vfs/sftpfs/internal.h @@ -91,7 +91,7 @@ void sftpfs_close_connection (struct vfs_s_super *super, const char *shutdown_me vfs_file_handler_t *sftpfs_fh_new (struct vfs_s_inode *ino, gboolean changed); void *sftpfs_opendir (const vfs_path_t * vpath, GError ** mcerror); -void *sftpfs_readdir (void *data, GError ** mcerror); +struct vfs_dirent *sftpfs_readdir (void *data, GError ** mcerror); int sftpfs_closedir (void *data, GError ** mcerror); int sftpfs_mkdir (const vfs_path_t * vpath, mode_t mode, GError ** mcerror); int sftpfs_rmdir (const vfs_path_t * vpath, GError ** mcerror); diff --git a/src/vfs/sftpfs/vfs_class.c b/src/vfs/sftpfs/vfs_class.c index 712cc1936..66be5ed4d 100644 --- a/src/vfs/sftpfs/vfs_class.c +++ b/src/vfs/sftpfs/vfs_class.c @@ -188,11 +188,11 @@ sftpfs_cb_opendir (const vfs_path_t * vpath) * @return information about direntry if success, NULL otherwise */ -static void * +static struct vfs_dirent * sftpfs_cb_readdir (void *data) { GError *mcerror = NULL; - union vfs_dirent *sftpfs_dirent; + struct vfs_dirent *sftpfs_dirent; if (tty_got_interrupt ()) { @@ -204,7 +204,7 @@ sftpfs_cb_readdir (void *data) if (!mc_error_message (&mcerror, NULL)) { if (sftpfs_dirent != NULL) - vfs_print_message (_("sftp: (Ctrl-G break) Listing... %s"), sftpfs_dirent->dent.d_name); + vfs_print_message (_("sftp: (Ctrl-G break) Listing... %s"), sftpfs_dirent->d_name); else vfs_print_message ("%s", _("sftp: Listing done.")); } diff --git a/src/vfs/smbfs/smbfs.c b/src/vfs/smbfs/smbfs.c index e91a18fbd..841e4c430 100644 --- a/src/vfs/smbfs/smbfs.c +++ b/src/vfs/smbfs/smbfs.c @@ -915,11 +915,10 @@ smbfs_free_dir (dir_entry * de) /* It's too slow to ask the server each time */ /* It now also sends the complete lstat information for each file */ -static void * +static struct vfs_dirent * smbfs_readdir (void *info) { - static union vfs_dirent smbfs_readdir_data; - static char *const dirent_dest = smbfs_readdir_data.dent.d_name; + struct vfs_dirent *dirent; opendir_info *smbfs_info = (opendir_info *) info; DEBUG (4, ("smbfs_readdir(%s)\n", smbfs_info->dirname)); @@ -937,10 +936,12 @@ smbfs_readdir (void *info) #endif return NULL; } - g_strlcpy (dirent_dest, smbfs_info->current->text, MC_MAXPATHLEN); + + dirent = vfs_dirent_init (NULL, smbfs_info->current->text, 0); /* FIXME: inode */ + smbfs_info->current = smbfs_info->current->next; - return &smbfs_readdir_data; + return dirent; } /* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/undelfs/undelfs.c b/src/vfs/undelfs/undelfs.c index 6e8a0685c..de1994363 100644 --- a/src/vfs/undelfs/undelfs.c +++ b/src/vfs/undelfs/undelfs.c @@ -400,11 +400,10 @@ undelfs_opendir (const vfs_path_t * vpath) /* --------------------------------------------------------------------------------------------- */ -static void * +static struct vfs_dirent * undelfs_readdir (void *vfs_info) { - static union vfs_dirent undelfs_readdir_data; - static char *const dirent_dest = undelfs_readdir_data.dent.d_name; + struct vfs_dirent *dirent; if (vfs_info != fs) { @@ -414,13 +413,18 @@ undelfs_readdir (void *vfs_info) if (readdir_ptr == num_delarray) return NULL; if (readdir_ptr < 0) - strcpy (dirent_dest, readdir_ptr == -2 ? "." : ".."); + dirent = vfs_dirent_init (NULL, readdir_ptr == -2 ? "." : "..", 0); /* FIXME: inode */ else + { + char dirent_dest[MC_MAXPATHLEN]; + g_snprintf (dirent_dest, MC_MAXPATHLEN, "%ld:%d", (long) delarray[readdir_ptr].ino, delarray[readdir_ptr].num_blocks); + dirent = vfs_dirent_init (NULL, dirent_dest, 0); /* FIXME: inode */ + } readdir_ptr++; - return &undelfs_readdir_data; + return dirent; } /* --------------------------------------------------------------------------------------------- */