(root)/
coreutils-9.4/
src/
sync.c
       1  /* sync - update the super block
       2     Copyright (C) 1994-2023 Free Software Foundation, Inc.
       3  
       4     This program is free software: you can redistribute it and/or modify
       5     it under the terms of the GNU General Public License as published by
       6     the Free Software Foundation, either version 3 of the License, or
       7     (at your option) any later version.
       8  
       9     This program is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12     GNU General Public License for more details.
      13  
      14     You should have received a copy of the GNU General Public License
      15     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      16  
      17  /* Written by Jim Meyering */
      18  
      19  #include <config.h>
      20  #include <getopt.h>
      21  #include <stdio.h>
      22  #include <sys/types.h>
      23  
      24  #include "system.h"
      25  
      26  /* The official name of this program (e.g., no 'g' prefix).  */
      27  #define PROGRAM_NAME "sync"
      28  
      29  #define AUTHORS                                 \
      30    proper_name ("Jim Meyering"),                 \
      31    proper_name ("Giuseppe Scrivano")
      32  
      33  #ifndef HAVE_SYNCFS
      34  # define HAVE_SYNCFS 0
      35  #endif
      36  
      37  enum sync_mode
      38  {
      39    MODE_FILE,
      40    MODE_DATA,
      41    MODE_FILE_SYSTEM,
      42    MODE_SYNC
      43  };
      44  
      45  static struct option const long_options[] =
      46  {
      47    {"data", no_argument, nullptr, 'd'},
      48    {"file-system", no_argument, nullptr, 'f'},
      49    {GETOPT_HELP_OPTION_DECL},
      50    {GETOPT_VERSION_OPTION_DECL},
      51    {nullptr, 0, nullptr, 0}
      52  };
      53  
      54  void
      55  usage (int status)
      56  {
      57    if (status != EXIT_SUCCESS)
      58      emit_try_help ();
      59    else
      60      {
      61        printf (_("Usage: %s [OPTION] [FILE]...\n"), program_name);
      62        fputs (_("\
      63  Synchronize cached writes to persistent storage\n\
      64  \n\
      65  If one or more files are specified, sync only them,\n\
      66  or their containing file systems.\n\
      67  \n\
      68  "), stdout);
      69  
      70        fputs (_("\
      71    -d, --data             sync only file data, no unneeded metadata\n\
      72  "), stdout);
      73        fputs (_("\
      74    -f, --file-system      sync the file systems that contain the files\n\
      75  "), stdout);
      76  
      77        fputs (HELP_OPTION_DESCRIPTION, stdout);
      78        fputs (VERSION_OPTION_DESCRIPTION, stdout);
      79        emit_ancillary_info (PROGRAM_NAME);
      80      }
      81    exit (status);
      82  }
      83  
      84  /* Sync the specified FILE, or file systems associated with FILE.
      85     Return 1 on success.  */
      86  
      87  static bool
      88  sync_arg (enum sync_mode mode, char const *file)
      89  {
      90    bool ret = true;
      91    int open_flags = O_RDONLY | O_NONBLOCK;
      92    int fd;
      93  
      94  #if defined _AIX || defined __CYGWIN__
      95    /* AIX 7.1, CYGWIN 2.9.0, fsync requires write access to file.  */
      96    if (mode == MODE_FILE)
      97      open_flags = O_WRONLY | O_NONBLOCK;
      98  #endif
      99  
     100    /* Note O_PATH might be supported with syncfs(),
     101       though as of Linux 3.18 is not.  */
     102    fd = open (file, open_flags);
     103    if (fd < 0)
     104      {
     105        /* Use the O_RDONLY errno, which is significant
     106           with directories for example.  */
     107        int rd_errno = errno;
     108        if (open_flags != (O_WRONLY | O_NONBLOCK))
     109          fd = open (file, O_WRONLY | O_NONBLOCK);
     110        if (fd < 0)
     111          {
     112            error (0, rd_errno, _("error opening %s"), quoteaf (file));
     113            return false;
     114          }
     115      }
     116  
     117    /* We used O_NONBLOCK above to not hang with fifos,
     118       so reset that here.  */
     119    int fdflags = fcntl (fd, F_GETFL);
     120    if (fdflags == -1
     121        || fcntl (fd, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
     122      {
     123        error (0, errno, _("couldn't reset non-blocking mode %s"),
     124               quoteaf (file));
     125        ret = false;
     126      }
     127  
     128    if (ret == true)
     129      {
     130        int sync_status = -1;
     131  
     132        switch (mode)
     133          {
     134          case MODE_DATA:
     135            sync_status = fdatasync (fd);
     136            break;
     137  
     138          case MODE_FILE:
     139            sync_status = fsync (fd);
     140            break;
     141  
     142  #if HAVE_SYNCFS
     143          case MODE_FILE_SYSTEM:
     144            sync_status = syncfs (fd);
     145            break;
     146  #endif
     147  
     148          default:
     149            unreachable ();
     150          }
     151  
     152        if (sync_status < 0)
     153          {
     154            error (0, errno, _("error syncing %s"), quoteaf (file));
     155            ret = false;
     156          }
     157      }
     158  
     159    if (close (fd) < 0)
     160      {
     161        error (0, errno, _("failed to close %s"), quoteaf (file));
     162        ret = false;
     163      }
     164  
     165    return ret;
     166  }
     167  
     168  int
     169  main (int argc, char **argv)
     170  {
     171    int c;
     172    bool args_specified;
     173    bool arg_data = false, arg_file_system = false;
     174    enum sync_mode mode;
     175    bool ok = true;
     176  
     177    initialize_main (&argc, &argv);
     178    set_program_name (argv[0]);
     179    setlocale (LC_ALL, "");
     180    bindtextdomain (PACKAGE, LOCALEDIR);
     181    textdomain (PACKAGE);
     182  
     183    atexit (close_stdout);
     184  
     185    while ((c = getopt_long (argc, argv, "df", long_options, nullptr))
     186           != -1)
     187      {
     188        switch (c)
     189          {
     190          case 'd':
     191            arg_data = true;
     192            break;
     193  
     194          case 'f':
     195            arg_file_system = true;
     196            break;
     197  
     198          case_GETOPT_HELP_CHAR;
     199  
     200          case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
     201  
     202          default:
     203            usage (EXIT_FAILURE);
     204          }
     205      }
     206  
     207    args_specified = optind < argc;
     208  
     209    if (arg_data && arg_file_system)
     210      error (EXIT_FAILURE, 0,
     211             _("cannot specify both --data and --file-system"));
     212  
     213    if (!args_specified && arg_data)
     214      error (EXIT_FAILURE, 0, _("--data needs at least one argument"));
     215  
     216    if (! args_specified || (arg_file_system && ! HAVE_SYNCFS))
     217      mode = MODE_SYNC;
     218    else if (arg_file_system)
     219      mode = MODE_FILE_SYSTEM;
     220    else if (! arg_data)
     221      mode = MODE_FILE;
     222    else
     223      mode = MODE_DATA;
     224  
     225    if (mode == MODE_SYNC)
     226      sync ();
     227    else
     228      {
     229        for (; optind < argc; optind++)
     230          ok &= sync_arg (mode, argv[optind]);
     231      }
     232  
     233    return ok ? EXIT_SUCCESS : EXIT_FAILURE;
     234  }