Manual pages: mc • mcdiff • mcedit • mcview

root/src/vfs/local/local.c

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

DEFINITIONS

This source file includes following definitions.
  1. local_open
  2. local_opendir
  3. local_readdir
  4. local_closedir
  5. local_stat
  6. local_lstat
  7. local_chmod
  8. local_chown
  9. local_fgetflags
  10. local_fsetflags
  11. local_utime
  12. local_readlink
  13. local_unlink
  14. local_symlink
  15. local_write
  16. local_rename
  17. local_chdir
  18. local_mknod
  19. local_link
  20. local_mkdir
  21. local_rmdir
  22. local_getlocalcopy
  23. local_ungetlocalcopy
  24. local_which
  25. local_read
  26. local_close
  27. local_errno
  28. local_fstat
  29. local_lseek
  30. local_nothingisopen
  31. vfs_init_localfs

   1 /*
   2    Virtual File System: local file system.
   3 
   4    Copyright (C) 1995-2026
   5    Free Software Foundation, Inc.
   6 
   7    This file is part of the Midnight Commander.
   8 
   9    The Midnight Commander is free software: you can redistribute it
  10    and/or modify it under the terms of the GNU General Public License as
  11    published by the Free Software Foundation, either version 3 of the License,
  12    or (at your option) any later version.
  13 
  14    The Midnight Commander is distributed in the hope that it will be useful,
  15    but WITHOUT ANY WARRANTY; without even the implied warranty of
  16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17    GNU General Public License for more details.
  18 
  19    You should have received a copy of the GNU General Public License
  20    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  21  */
  22 
  23 /**
  24  * \file
  25  * \brief Source: local FS
  26  */
  27 
  28 #include <config.h>
  29 
  30 #include <errno.h>
  31 #include <sys/types.h>
  32 #include <unistd.h>
  33 #include <stdio.h>
  34 #include <string.h>
  35 #ifdef ENABLE_EXT2FS_ATTR
  36 #include <e2p/e2p.h>  // fgetflags(), fsetflags()
  37 #endif
  38 
  39 #include "lib/global.h"
  40 
  41 #include "lib/vfs/xdirentry.h"  // vfs_s_subclass
  42 #include "lib/vfs/utilvfs.h"
  43 
  44 #include "local.h"
  45 
  46 /*** global variables ****************************************************************************/
  47 
  48 /*** file scope macro definitions ****************************************************************/
  49 
  50 /*** file scope type declarations ****************************************************************/
  51 
  52 /*** forward declarations (file scope functions) *************************************************/
  53 
  54 /*** file scope variables ************************************************************************/
  55 
  56 static struct vfs_s_subclass local_subclass;
  57 static struct vfs_class *vfs_local_ops = VFS_CLASS (&local_subclass);
  58 
  59 /* --------------------------------------------------------------------------------------------- */
  60 /*** file scope functions ************************************************************************/
  61 /* --------------------------------------------------------------------------------------------- */
  62 
  63 static void *
  64 local_open (const vfs_path_t *vpath, int flags, mode_t mode)
     /* [previous][next][first][last][top][bottom][index][help]  */
  65 {
  66     int *local_info;
  67     int fd;
  68     const char *path;
  69 
  70     path = vfs_path_get_last_path_str (vpath);
  71     fd = open (path, NO_LINEAR (flags), mode);
  72     if (fd == -1)
  73         return NULL;
  74 
  75     local_info = g_new (int, 1);
  76     *local_info = fd;
  77 
  78     return local_info;
  79 }
  80 
  81 /* --------------------------------------------------------------------------------------------- */
  82 
  83 static void *
  84 local_opendir (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
  85 {
  86     DIR *dir = NULL;
  87 
  88     const char *path = vfs_path_get_last_path_str (vpath);
  89 
  90     /* According to POSIX, `opendir` and `readdir` can't return EINTR (unlike `closedir`).
  91      *
  92      * Unfortunately, in practice, at least `readdir` is known to return EINTR on Linux >= 5.1 on
  93      * networked filesystems such as CIFS. Networked FUSE-based systems appear to be affected as
  94      * well. What's worse is that when you retry `readdir`, the file list still comes out empty.
  95      *
  96      * So our only option seems to be to try `readdir` immediately after `opendir` and reopen the
  97      * directory if it failed with EINTR, which usually leads to correct file listing, or use
  98      * `rewinddir` otherwise, even if `readdir` failed, since FreeBSD and macOS may have advanced
  99      * the stream anyway.
 100      *
 101      * However, this has caused problems on FUSE-based systems that do not properly implement
 102      * `rewinddir` in the past. No silver bullet...
 103      */
 104     while (dir == NULL)
 105     {
 106         dir = opendir (path);
 107         if (dir == NULL)
 108         {
 109             // Unlike readdir(), opendir() always sets errno on failure, so no reset is needed
 110             if (errno == EINTR)
 111                 continue;
 112 
 113             return NULL;
 114         }
 115 
 116         // Reset errno before readdir to avoid reading a stale EINTR (#5156)
 117         errno = 0;
 118 
 119         if (readdir (dir) == NULL && errno == EINTR)
 120         {
 121             closedir (dir);
 122             dir = NULL;
 123         }
 124         else
 125             rewinddir (dir);
 126     }
 127 
 128     DIR **local_info = (DIR **) g_new (DIR *, 1);
 129     *local_info = dir;
 130 
 131     return local_info;
 132 }
 133 
 134 /* --------------------------------------------------------------------------------------------- */
 135 
 136 static struct vfs_dirent *
 137 local_readdir (void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 138 {
 139     struct dirent *d;
 140     unsigned char type;
 141 
 142     // Retry if interrupted, resetting errno to avoid reading a stale EINTR (#5156). FreeBSD and
 143     // macOS may lose the entries read before the interruption, but rewinding would repeat the
 144     // ones already returned.
 145     do
 146     {
 147         errno = 0;
 148         d = readdir (*(DIR **) data);
 149     }
 150     while (d == NULL && errno == EINTR);
 151 
 152     if (d == NULL)
 153         return NULL;
 154 
 155 #ifdef HAVE_STRUCT_DIRENT_D_TYPE
 156     type = d->d_type;
 157 #else
 158     type = DT_UNKNOWN;
 159 #endif
 160 
 161     return vfs_dirent_init (NULL, d->d_name, d->d_ino, type);
 162 }
 163 
 164 /* --------------------------------------------------------------------------------------------- */
 165 
 166 static int
 167 local_closedir (void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 168 {
 169     int i;
 170 
 171     i = closedir (*(DIR **) data);
 172     g_free (data);
 173     return i;
 174 }
 175 
 176 /* --------------------------------------------------------------------------------------------- */
 177 
 178 static int
 179 local_stat (const vfs_path_t *vpath, struct stat *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
 180 {
 181     const char *path;
 182 
 183     path = vfs_path_get_last_path_str (vpath);
 184     return stat (path, buf);
 185 }
 186 
 187 /* --------------------------------------------------------------------------------------------- */
 188 
 189 static int
 190 local_lstat (const vfs_path_t *vpath, struct stat *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
 191 {
 192     const char *path;
 193 
 194     path = vfs_path_get_last_path_str (vpath);
 195 #ifndef HAVE_STATLSTAT
 196     return lstat (path, buf);
 197 #else
 198     return statlstat (path, buf);
 199 #endif
 200 }
 201 
 202 /* --------------------------------------------------------------------------------------------- */
 203 
 204 static int
 205 local_chmod (const vfs_path_t *vpath, mode_t mode)
     /* [previous][next][first][last][top][bottom][index][help]  */
 206 {
 207     const char *path;
 208 
 209     path = vfs_path_get_last_path_str (vpath);
 210     return chmod (path, mode);
 211 }
 212 
 213 /* --------------------------------------------------------------------------------------------- */
 214 
 215 static int
 216 local_chown (const vfs_path_t *vpath, uid_t owner, gid_t group)
     /* [previous][next][first][last][top][bottom][index][help]  */
 217 {
 218     const char *path;
 219 
 220     path = vfs_path_get_last_path_str (vpath);
 221     return chown (path, owner, group);
 222 }
 223 
 224 /* --------------------------------------------------------------------------------------------- */
 225 
 226 #ifdef ENABLE_EXT2FS_ATTR
 227 
 228 static int
 229 local_fgetflags (const vfs_path_t *vpath, unsigned long *flags)
     /* [previous][next][first][last][top][bottom][index][help]  */
 230 {
 231     const char *path;
 232 
 233     path = vfs_path_get_last_path_str (vpath);
 234     return fgetflags (path, flags);
 235 }
 236 
 237 /* --------------------------------------------------------------------------------------------- */
 238 
 239 static int
 240 local_fsetflags (const vfs_path_t *vpath, unsigned long flags)
     /* [previous][next][first][last][top][bottom][index][help]  */
 241 {
 242     const char *path;
 243 
 244     path = vfs_path_get_last_path_str (vpath);
 245     return fsetflags (path, flags);
 246 }
 247 
 248 #endif
 249 
 250 /* --------------------------------------------------------------------------------------------- */
 251 
 252 static int
 253 local_utime (const vfs_path_t *vpath, mc_timesbuf_t *times)
     /* [previous][next][first][last][top][bottom][index][help]  */
 254 {
 255     return vfs_utime (vfs_path_get_last_path_str (vpath), times);
 256 }
 257 
 258 /* --------------------------------------------------------------------------------------------- */
 259 
 260 static int
 261 local_readlink (const vfs_path_t *vpath, char *buf, size_t size)
     /* [previous][next][first][last][top][bottom][index][help]  */
 262 {
 263     const char *path;
 264 
 265     path = vfs_path_get_last_path_str (vpath);
 266     return readlink (path, buf, size);
 267 }
 268 
 269 /* --------------------------------------------------------------------------------------------- */
 270 
 271 static int
 272 local_unlink (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 273 {
 274     const char *path;
 275 
 276     path = vfs_path_get_last_path_str (vpath);
 277     return unlink (path);
 278 }
 279 
 280 /* --------------------------------------------------------------------------------------------- */
 281 
 282 static int
 283 local_symlink (const vfs_path_t *vpath1, const vfs_path_t *vpath2)
     /* [previous][next][first][last][top][bottom][index][help]  */
 284 {
 285     const char *path1, *path2;
 286 
 287     path1 = vfs_path_get_last_path_str (vpath1);
 288     path2 = vfs_path_get_last_path_str (vpath2);
 289     return symlink (path1, path2);
 290 }
 291 
 292 /* --------------------------------------------------------------------------------------------- */
 293 
 294 static ssize_t
 295 local_write (void *data, const char *buf, size_t nbyte)
     /* [previous][next][first][last][top][bottom][index][help]  */
 296 {
 297     int fd;
 298     int n;
 299 
 300     if (data == NULL)
 301         return (-1);
 302 
 303     fd = *(int *) data;
 304 
 305     while ((n = write (fd, buf, nbyte)) == -1)
 306     {
 307 #ifdef EAGAIN
 308         if (errno == EAGAIN)
 309             continue;
 310 #endif
 311 #ifdef EINTR
 312         if (errno == EINTR)
 313             continue;
 314 #endif
 315         break;
 316     }
 317 
 318     return n;
 319 }
 320 
 321 /* --------------------------------------------------------------------------------------------- */
 322 
 323 static int
 324 local_rename (const vfs_path_t *vpath1, const vfs_path_t *vpath2)
     /* [previous][next][first][last][top][bottom][index][help]  */
 325 {
 326     const char *path1, *path2;
 327 
 328     path1 = vfs_path_get_last_path_str (vpath1);
 329     path2 = vfs_path_get_last_path_str (vpath2);
 330     return rename (path1, path2);
 331 }
 332 
 333 /* --------------------------------------------------------------------------------------------- */
 334 
 335 static int
 336 local_chdir (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 337 {
 338     const char *path;
 339 
 340     path = vfs_path_get_last_path_str (vpath);
 341     return chdir (path);
 342 }
 343 
 344 /* --------------------------------------------------------------------------------------------- */
 345 
 346 static int
 347 local_mknod (const vfs_path_t *vpath, mode_t mode, dev_t dev)
     /* [previous][next][first][last][top][bottom][index][help]  */
 348 {
 349     const char *path;
 350 
 351     path = vfs_path_get_last_path_str (vpath);
 352     return mknod (path, mode, dev);
 353 }
 354 
 355 /* --------------------------------------------------------------------------------------------- */
 356 
 357 static int
 358 local_link (const vfs_path_t *vpath1, const vfs_path_t *vpath2)
     /* [previous][next][first][last][top][bottom][index][help]  */
 359 {
 360     const char *path1, *path2;
 361 
 362     path1 = vfs_path_get_last_path_str (vpath1);
 363     path2 = vfs_path_get_last_path_str (vpath2);
 364     return link (path1, path2);
 365 }
 366 
 367 /* --------------------------------------------------------------------------------------------- */
 368 
 369 static int
 370 local_mkdir (const vfs_path_t *vpath, mode_t mode)
     /* [previous][next][first][last][top][bottom][index][help]  */
 371 {
 372     const char *path;
 373 
 374     path = vfs_path_get_last_path_str (vpath);
 375     return mkdir (path, mode);
 376 }
 377 
 378 /* --------------------------------------------------------------------------------------------- */
 379 
 380 static int
 381 local_rmdir (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 382 {
 383     const char *path;
 384 
 385     path = vfs_path_get_last_path_str (vpath);
 386     return rmdir (path);
 387 }
 388 
 389 /* --------------------------------------------------------------------------------------------- */
 390 
 391 static vfs_path_t *
 392 local_getlocalcopy (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 393 {
 394     return vfs_path_clone (vpath);
 395 }
 396 
 397 /* --------------------------------------------------------------------------------------------- */
 398 
 399 static int
 400 local_ungetlocalcopy (const vfs_path_t *vpath, const vfs_path_t *local, gboolean has_changed)
     /* [previous][next][first][last][top][bottom][index][help]  */
 401 {
 402     (void) vpath;
 403     (void) local;
 404     (void) has_changed;
 405 
 406     return 0;
 407 }
 408 
 409 /* --------------------------------------------------------------------------------------------- */
 410 
 411 static int
 412 local_which (struct vfs_class *me, const char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
 413 {
 414     (void) me;
 415     (void) path;
 416 
 417     return 0;  // Every path which other systems do not like is expected to be ours
 418 }
 419 
 420 /* --------------------------------------------------------------------------------------------- */
 421 /*** public functions ****************************************************************************/
 422 /* --------------------------------------------------------------------------------------------- */
 423 
 424 ssize_t
 425 local_read (void *data, char *buffer, size_t count)
     /* [previous][next][first][last][top][bottom][index][help]  */
 426 {
 427     int n;
 428     int fd;
 429 
 430     if (data == NULL)
 431         return (-1);
 432 
 433     fd = *(int *) data;
 434 
 435     while ((n = read (fd, buffer, count)) == -1)
 436     {
 437 #ifdef EAGAIN
 438         if (errno == EAGAIN)
 439             continue;
 440 #endif
 441 #ifdef EINTR
 442         if (errno == EINTR)
 443             continue;
 444 #endif
 445         return (-1);
 446     }
 447 
 448     return n;
 449 }
 450 
 451 /* --------------------------------------------------------------------------------------------- */
 452 
 453 int
 454 local_close (void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 455 {
 456     int fd;
 457 
 458     if (data == NULL)
 459         return (-1);
 460 
 461     fd = *(int *) data;
 462     g_free (data);
 463     return close (fd);
 464 }
 465 
 466 /* --------------------------------------------------------------------------------------------- */
 467 
 468 int
 469 local_errno (struct vfs_class *me)
     /* [previous][next][first][last][top][bottom][index][help]  */
 470 {
 471     (void) me;
 472     return errno;
 473 }
 474 
 475 /* --------------------------------------------------------------------------------------------- */
 476 
 477 int
 478 local_fstat (void *data, struct stat *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
 479 {
 480     int fd = *(int *) data;
 481 
 482     return fstat (fd, buf);
 483 }
 484 
 485 /* --------------------------------------------------------------------------------------------- */
 486 
 487 off_t
 488 local_lseek (void *data, off_t offset, int whence)
     /* [previous][next][first][last][top][bottom][index][help]  */
 489 {
 490     int fd = *(int *) data;
 491 
 492     return lseek (fd, offset, whence);
 493 }
 494 
 495 /* --------------------------------------------------------------------------------------------- */
 496 
 497 static gboolean
 498 local_nothingisopen (vfsid id)
     /* [previous][next][first][last][top][bottom][index][help]  */
 499 {
 500     (void) id;
 501 
 502     return TRUE;
 503 }
 504 
 505 /* --------------------------------------------------------------------------------------------- */
 506 
 507 void
 508 vfs_init_localfs (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 509 {
 510     // NULLize vfs_s_subclass members
 511     memset (&local_subclass, 0, sizeof (local_subclass));
 512 
 513     vfs_init_class (vfs_local_ops, "localfs", VFSF_LOCAL, NULL);
 514     vfs_local_ops->which = local_which;
 515     vfs_local_ops->open = local_open;
 516     vfs_local_ops->close = local_close;
 517     vfs_local_ops->read = local_read;
 518     vfs_local_ops->write = local_write;
 519     vfs_local_ops->opendir = local_opendir;
 520     vfs_local_ops->readdir = local_readdir;
 521     vfs_local_ops->closedir = local_closedir;
 522     vfs_local_ops->stat = local_stat;
 523     vfs_local_ops->lstat = local_lstat;
 524     vfs_local_ops->fstat = local_fstat;
 525     vfs_local_ops->chmod = local_chmod;
 526     vfs_local_ops->chown = local_chown;
 527 #ifdef ENABLE_EXT2FS_ATTR
 528     vfs_local_ops->fgetflags = local_fgetflags;
 529     vfs_local_ops->fsetflags = local_fsetflags;
 530 #endif
 531     vfs_local_ops->utime = local_utime;
 532     vfs_local_ops->readlink = local_readlink;
 533     vfs_local_ops->symlink = local_symlink;
 534     vfs_local_ops->link = local_link;
 535     vfs_local_ops->unlink = local_unlink;
 536     vfs_local_ops->rename = local_rename;
 537     vfs_local_ops->chdir = local_chdir;
 538     vfs_local_ops->ferrno = local_errno;
 539     vfs_local_ops->lseek = local_lseek;
 540     vfs_local_ops->mknod = local_mknod;
 541     vfs_local_ops->getlocalcopy = local_getlocalcopy;
 542     vfs_local_ops->ungetlocalcopy = local_ungetlocalcopy;
 543     vfs_local_ops->mkdir = local_mkdir;
 544     vfs_local_ops->rmdir = local_rmdir;
 545     vfs_local_ops->nothingisopen = local_nothingisopen;
 546     vfs_register_class (vfs_local_ops);
 547 }
 548 
 549 /* --------------------------------------------------------------------------------------------- */

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