root/lib/vfs/utilvfs.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. vfs_get_local_username
  2. vfs_finduid
  3. vfs_findgid
  4. vfs_mkstemps
  5. vfs_url_split
  6. vfs_die
  7. vfs_get_password
  8. vfs_utime
  9. vfs_get_timespecs_from_timesbuf
  10. vfs_get_timesbuf_from_stat
  11. vfs_copy_stat_times
  12. vfs_zero_stat_times

   1 /*
   2    Utilities for VFS modules.
   3 
   4    Copyright (C) 1988-2024
   5    Free Software Foundation, Inc.
   6 
   7    Copyright (C) 1995, 1996 Miguel de Icaza
   8 
   9    This file is part of the Midnight Commander.
  10 
  11    The Midnight Commander is free software: you can redistribute it
  12    and/or modify it under the terms of the GNU General Public License as
  13    published by the Free Software Foundation, either version 3 of the License,
  14    or (at your option) any later version.
  15 
  16    The Midnight Commander is distributed in the hope that it will be useful,
  17    but WITHOUT ANY WARRANTY; without even the implied warranty of
  18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19    GNU General Public License for more details.
  20 
  21    You should have received a copy of the GNU General Public License
  22    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  23  */
  24 
  25 /**
  26  * \file
  27  * \brief Source: Utilities for VFS modules
  28  * \author Miguel de Icaza
  29  * \date 1995, 1996
  30  */
  31 
  32 #include <config.h>
  33 
  34 #include <ctype.h>
  35 #include <sys/types.h>
  36 #include <pwd.h>
  37 #include <grp.h>
  38 #include <stdlib.h>
  39 #include <string.h>
  40 
  41 #if !defined (HAVE_UTIMENSAT) && defined (HAVE_UTIME_H)
  42 #include <utime.h>
  43 #endif
  44 
  45 #include "lib/global.h"
  46 #include "lib/unixcompat.h"
  47 #include "lib/widget.h"         /* message() */
  48 #include "lib/strutil.h"        /* INVALID_CONV */
  49 
  50 #include "vfs.h"
  51 #include "utilvfs.h"
  52 
  53 /*** global variables ****************************************************************************/
  54 
  55 /*** file scope macro definitions ****************************************************************/
  56 
  57 #ifndef TUNMLEN
  58 #define TUNMLEN 256
  59 #endif
  60 #ifndef TGNMLEN
  61 #define TGNMLEN 256
  62 #endif
  63 
  64 #define MC_HISTORY_VFS_PASSWORD       "mc.vfs.password"
  65 
  66 /*
  67  * FIXME2, the "-993" is to reduce the chance of a hit on the first lookup.
  68  */
  69 #define GUID_DEFAULT_CONST -993
  70 
  71 /*** file scope type declarations ****************************************************************/
  72 
  73 /*** forward declarations (file scope functions) *************************************************/
  74 
  75 /*** file scope variables ************************************************************************/
  76 
  77 /* --------------------------------------------------------------------------------------------- */
  78 /*** file scope functions ************************************************************************/
  79 /* --------------------------------------------------------------------------------------------- */
  80 
  81 /* --------------------------------------------------------------------------------------------- */
  82 /*** public functions ****************************************************************************/
  83 /* --------------------------------------------------------------------------------------------- */
  84 /** Get current username
  85  *
  86  * @return g_malloc()ed string with the name of the currently logged in
  87  *         user ("anonymous" if uid is not registered in the system)
  88  */
  89 
  90 char *
  91 vfs_get_local_username (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
  92 {
  93     struct passwd *p_i;
  94 
  95     p_i = getpwuid (geteuid ());
  96 
  97     /* Unknown UID, strange */
  98     return (p_i != NULL && p_i->pw_name != NULL) ? g_strdup (p_i->pw_name) : g_strdup ("anonymous");
  99 }
 100 
 101 /* --------------------------------------------------------------------------------------------- */
 102 /**
 103  * Look up a user or group name from a uid/gid, maintaining a cache.
 104  * FIXME, for now it's a one-entry cache.
 105  * This file should be modified for non-unix systems to do something
 106  * reasonable.
 107  */
 108 
 109 int
 110 vfs_finduid (const char *uname)
     /* [previous][next][first][last][top][bottom][index][help]  */
 111 {
 112     static int saveuid = GUID_DEFAULT_CONST;
 113     static char saveuname[TUNMLEN] = "\0";
 114 
 115     size_t uname_len;
 116 
 117     uname_len = strlen (uname);
 118 
 119     if (uname[0] != saveuname[0]        /* Quick test w/o proc call */
 120         || strncmp (uname, saveuname, MIN (uname_len, TUNMLEN - 1)) != 0)
 121     {
 122         struct passwd *pw;
 123 
 124         g_strlcpy (saveuname, uname, TUNMLEN);
 125         pw = getpwnam (uname);
 126         if (pw != NULL)
 127             saveuid = pw->pw_uid;
 128         else
 129         {
 130             static int my_uid = GUID_DEFAULT_CONST;
 131 
 132             if (my_uid < 0)
 133                 my_uid = getuid ();
 134 
 135             saveuid = my_uid;
 136         }
 137     }
 138 
 139     return saveuid;
 140 }
 141 
 142 /* --------------------------------------------------------------------------------------------- */
 143 
 144 int
 145 vfs_findgid (const char *gname)
     /* [previous][next][first][last][top][bottom][index][help]  */
 146 {
 147     static int savegid = GUID_DEFAULT_CONST;
 148     static char savegname[TGNMLEN] = "\0";
 149 
 150     size_t gname_len;
 151 
 152     gname_len = strlen (gname);
 153 
 154     if (gname[0] != savegname[0]        /* Quick test w/o proc call */
 155         || strncmp (gname, savegname, MIN (gname_len, TGNMLEN - 1)) != 0)
 156     {
 157         struct group *gr;
 158 
 159         g_strlcpy (savegname, gname, TGNMLEN);
 160         gr = getgrnam (gname);
 161         if (gr != NULL)
 162             savegid = gr->gr_gid;
 163         else
 164         {
 165             static int my_gid = GUID_DEFAULT_CONST;
 166 
 167             if (my_gid < 0)
 168                 my_gid = getgid ();
 169 
 170             savegid = my_gid;
 171         }
 172     }
 173 
 174     return savegid;
 175 }
 176 
 177 /* --------------------------------------------------------------------------------------------- */
 178 /**
 179  * Create a temporary file with a name resembling the original.
 180  * This is needed e.g. for local copies requested by extfs.
 181  * Some extfs scripts may look at the extension.
 182  * We also protect stupid scripts against dangerous names.
 183  */
 184 
 185 int
 186 vfs_mkstemps (vfs_path_t **pname_vpath, const char *prefix, const char *param_basename)
     /* [previous][next][first][last][top][bottom][index][help]  */
 187 {
 188     const char *p;
 189     GString *suffix;
 190     int shift;
 191     int fd;
 192 
 193     /* Strip directories */
 194     p = strrchr (param_basename, PATH_SEP);
 195     if (p == NULL)
 196         p = param_basename;
 197     else
 198         p++;
 199 
 200     /* Protection against very long names */
 201     shift = strlen (p) - (MC_MAXPATHLEN - 16);
 202     if (shift > 0)
 203         p += shift;
 204 
 205     suffix = g_string_sized_new (32);
 206 
 207     /* Protection against unusual characters */
 208     for (; *p != '\0' && *p != '#'; p++)
 209         if (strchr (".-_@", *p) != NULL || g_ascii_isalnum (*p))
 210             g_string_append_c (suffix, *p);
 211 
 212     fd = mc_mkstemps (pname_vpath, prefix, suffix->str);
 213     g_string_free (suffix, TRUE);
 214 
 215     return fd;
 216 }
 217 
 218 /* --------------------------------------------------------------------------------------------- */
 219 /**  Extract the hostname and username from the path
 220  *
 221  * Format of the path is [user@]hostname:port/remote-dir, e.g.:
 222  *
 223  * ftp://sunsite.unc.edu/pub/linux
 224  * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
 225  * ftp://tsx-11.mit.edu:8192/
 226  * ftp://joe@foo.edu:11321/private
 227  * ftp://joe:password@foo.se
 228  *
 229  * @param path is an input string to be parsed
 230  * @param default_port is an input default port
 231  * @param flags are parsing modifier flags (@see vfs_url_flags_t)
 232  *
 233  * @return g_malloc()ed url info.
 234  *         If the user is empty, e.g. ftp://@roxanne/private, and URL_USE_ANONYMOUS
 235  *         is not set, then the current login name is supplied.
 236  *         Return value is a g_malloc()ed structure with the pathname relative to the
 237  *         host.
 238  */
 239 
 240 vfs_path_element_t *
 241 vfs_url_split (const char *path, int default_port, vfs_url_flags_t flags)
     /* [previous][next][first][last][top][bottom][index][help]  */
 242 {
 243     vfs_path_element_t *path_element;
 244 
 245     char *pcopy;
 246     size_t pcopy_len;
 247     const char *pend;
 248     char *colon, *at, *rest;
 249 
 250     path_element = g_new0 (vfs_path_element_t, 1);
 251     path_element->port = default_port;
 252 
 253     pcopy_len = strlen (path);
 254     pcopy = g_strndup (path, pcopy_len);
 255     pend = pcopy + pcopy_len;
 256 
 257     if ((flags & URL_NOSLASH) == 0)
 258     {
 259         char *dir;
 260 
 261         /* locate path component */
 262         dir = strchr (pcopy, PATH_SEP);
 263 
 264         if (dir == NULL)
 265             path_element->path = g_strdup (PATH_SEP_STR);
 266         else
 267         {
 268             path_element->path = g_strndup (dir, pcopy_len - (size_t) (dir - pcopy));
 269             *dir = '\0';
 270         }
 271     }
 272 
 273     /* search for any possible user */
 274     at = strrchr (pcopy, '@');
 275 
 276     /* We have a username */
 277     if (at == NULL)
 278         rest = pcopy;
 279     else
 280     {
 281         char *inner_colon;
 282 
 283         *at = '\0';
 284         inner_colon = strchr (pcopy, ':');
 285         if (inner_colon != NULL)
 286         {
 287             *inner_colon = '\0';
 288             inner_colon++;
 289             path_element->password = g_strdup (inner_colon);
 290         }
 291 
 292         if (*pcopy != '\0')
 293             path_element->user = g_strdup (pcopy);
 294 
 295         if (pend == at + 1)
 296             rest = at;
 297         else
 298             rest = at + 1;
 299     }
 300 
 301     if ((flags & URL_USE_ANONYMOUS) == 0)
 302     {
 303         g_free (path_element->user);
 304         path_element->user = vfs_get_local_username ();
 305     }
 306     /* Check if the host comes with a port spec, if so, chop it */
 307     if (*rest != '[')
 308         colon = strchr (rest, ':');
 309     else
 310     {
 311         colon = strchr (++rest, ']');
 312         if (colon != NULL)
 313         {
 314             colon[0] = '\0';
 315             colon[1] = '\0';
 316             colon++;
 317         }
 318         else
 319         {
 320             vfs_path_element_free (path_element);
 321             g_free (pcopy);
 322             return NULL;
 323         }
 324     }
 325 
 326     if (colon != NULL)
 327     {
 328         *colon = '\0';
 329         /* cppcheck-suppress invalidscanf */
 330         if (sscanf (colon + 1, "%d", &path_element->port) == 1)
 331         {
 332             if (path_element->port <= 0 || path_element->port >= 65536)
 333                 path_element->port = default_port;
 334         }
 335         else
 336             while (*(++colon) != '\0')
 337             {
 338                 switch (*colon)
 339                 {
 340                 case 'C':
 341                     path_element->port = 1;
 342                     break;
 343                 case 'r':
 344                     path_element->port = 2;
 345                     break;
 346                 default:
 347                     break;
 348                 }
 349             }
 350     }
 351 
 352     path_element->host = g_strdup (rest);
 353     g_free (pcopy);
 354 #ifdef HAVE_CHARSET
 355     path_element->dir.converter = INVALID_CONV;
 356 #endif
 357 
 358     return path_element;
 359 }
 360 
 361 /* --------------------------------------------------------------------------------------------- */
 362 
 363 void __attribute__((noreturn)) vfs_die (const char *m)
     /* [previous][next][first][last][top][bottom][index][help]  */
 364 {
 365     message (D_ERROR, _("Internal error:"), "%s", m);
 366     exit (EXIT_FAILURE);
 367 }
 368 
 369 /* --------------------------------------------------------------------------------------------- */
 370 
 371 char *
 372 vfs_get_password (const char *msg)
     /* [previous][next][first][last][top][bottom][index][help]  */
 373 {
 374     return input_dialog (msg, _("Password:"), MC_HISTORY_VFS_PASSWORD, INPUT_PASSWORD,
 375                          INPUT_COMPLETE_NONE);
 376 }
 377 
 378 /* --------------------------------------------------------------------------------------------- */
 379 
 380 int
 381 vfs_utime (const char *path, mc_timesbuf_t *times)
     /* [previous][next][first][last][top][bottom][index][help]  */
 382 {
 383 #ifdef HAVE_UTIMENSAT
 384     return utimensat (AT_FDCWD, path, *times, AT_SYMLINK_NOFOLLOW);
 385 #else
 386     return utime (path, times);
 387 #endif
 388 }
 389 
 390 /* --------------------------------------------------------------------------------------------- */
 391 
 392 void
 393 vfs_get_timespecs_from_timesbuf (mc_timesbuf_t *times, mc_timespec_t *atime, mc_timespec_t *mtime)
     /* [previous][next][first][last][top][bottom][index][help]  */
 394 {
 395 #ifdef HAVE_UTIMENSAT
 396     atime->tv_sec = (*times)[0].tv_sec;
 397     atime->tv_nsec = (*times)[0].tv_nsec;
 398     mtime->tv_sec = (*times)[1].tv_sec;
 399     mtime->tv_nsec = (*times)[1].tv_nsec;
 400 #else
 401     atime->tv_sec = times->actime;
 402     atime->tv_nsec = 0;
 403     mtime->tv_sec = times->modtime;
 404     mtime->tv_nsec = 0;
 405 #endif
 406 }
 407 
 408 /* --------------------------------------------------------------------------------------------- */
 409 
 410 void
 411 vfs_get_timesbuf_from_stat (const struct stat *s, mc_timesbuf_t *times)
     /* [previous][next][first][last][top][bottom][index][help]  */
 412 {
 413 #ifdef HAVE_UTIMENSAT
 414 #ifdef HAVE_STRUCT_STAT_ST_MTIM
 415     /* POSIX IEEE Std 1003.1-2008 should be the preferred way
 416      *
 417      * AIX has internal type st_timespec_t conflicting with timespec, so assign per field, for details see:
 418      * https://github.com/libuv/libuv/pull/4404
 419      */
 420     (*times)[0].tv_sec = s->st_atim.tv_sec;
 421     (*times)[0].tv_nsec = s->st_atim.tv_nsec;
 422     (*times)[1].tv_sec = s->st_mtim.tv_sec;
 423     (*times)[1].tv_nsec = s->st_mtim.tv_nsec;
 424 #elif HAVE_STRUCT_STAT_ST_MTIMESPEC
 425     /* Modern BSD solution */
 426     (*times)[0] = s->st_atimespec;
 427     (*times)[1] = s->st_mtimespec;
 428 #elif HAVE_STRUCT_STAT_ST_MTIMENSEC
 429     /* Legacy BSD solution */
 430     (*times)[0].tv_sec = s->st_atime;
 431     (*times)[0].tv_nsec = s->st_atimensec;
 432     (*times)[1].tv_sec = s->st_mtime;
 433     (*times)[1].tv_nsec = s->st_mtimensec;
 434 #else
 435 #error "Found utimensat for nanosecond timestamps, but unsupported struct stat format!"
 436 #endif
 437 #else
 438     times->actime = s->st_atime;
 439     times->modtime = s->st_mtime;
 440 #endif
 441 }
 442 
 443 /* --------------------------------------------------------------------------------------------- */
 444 
 445 void
 446 vfs_copy_stat_times (const struct stat *src, struct stat *dst)
     /* [previous][next][first][last][top][bottom][index][help]  */
 447 {
 448     dst->st_atime = src->st_atime;
 449     dst->st_mtime = src->st_mtime;
 450     dst->st_ctime = src->st_ctime;
 451 
 452 #ifdef HAVE_STRUCT_STAT_ST_MTIM
 453     dst->st_atim.tv_nsec = src->st_atim.tv_nsec;
 454     dst->st_mtim.tv_nsec = src->st_mtim.tv_nsec;
 455     dst->st_ctim.tv_nsec = src->st_ctim.tv_nsec;
 456 #elif HAVE_STRUCT_STAT_ST_MTIMESPEC
 457     dst->st_atimespec.tv_nsec = src->st_atimespec.tv_nsec;
 458     dst->st_mtimespec.tv_nsec = src->st_mtimespec.tv_nsec;
 459     dst->st_ctimespec.tv_nsec = src->st_ctimespec.tv_nsec;
 460 #elif HAVE_STRUCT_STAT_ST_MTIMENSEC
 461     dst->st_atimensec = src->st_atimensec;
 462     dst->st_mtimensec = src->st_mtimensec;
 463     dst->st_ctimensec = src->st_ctimensec;
 464 #endif
 465 }
 466 
 467 /* --------------------------------------------------------------------------------------------- */
 468 
 469 void
 470 vfs_zero_stat_times (struct stat *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 471 {
 472     const struct stat empty = { 0 };
 473 
 474     vfs_copy_stat_times (&empty, s);
 475 }
 476 
 477 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */