>From 12c6f0fd7f44133a2af8950c69b2bfa46ea5d3a4 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Sun, 25 Jan 2015 01:33:45 +0100 Subject: [PATCH] sync: support syncing specified arguments * AUTHORS: Add myself to sync's authors. * NEWS: Mention the new feature. * m4/jm-macros.m4 (coreutils_MACROS): Check for syncfs(). * man/sync.x: Add references to syncfs, fsync and fdatasync. * doc/coreutils.texi (sync invocation): Document the new feature. * src/sync.c: Include "quote.h". (AUTHORS): Include myself. (MODE_FILE, MODE_DATA, MODE_FILE_SYSTEM, MODE_SYNC): New enum values. (long_options): Define. (sync_arg): New function. (usage): Describe that arguments are now accepted. (main): Add arguments parsing and add support for fsync(2), fdatasync(2) and syncfs(2). --- AUTHORS | 2 +- NEWS | 3 + doc/coreutils.texi | 34 +++++++++-- m4/jm-macros.m4 | 1 + man/sync.x | 2 +- src/sync.c | 169 +++++++++++++++++++++++++++++++++++++++++++++++++---- tests/local.mk | 1 + tests/misc/sync.sh | 43 ++++++++++++++ 8 files changed, 236 insertions(+), 19 deletions(-) create mode 100755 tests/misc/sync.sh diff --git a/AUTHORS b/AUTHORS index 0296830..64c11d7 100644 --- a/AUTHORS +++ b/AUTHORS @@ -83,7 +83,7 @@ stat: Michael Meskes stdbuf: Pádraig Brady stty: David MacKenzie sum: Kayvan Aghaiepour, David MacKenzie -sync: Jim Meyering +sync: Jim Meyering, Giuseppe Scrivano tac: Jay Lepreau, David MacKenzie tail: Paul Rubin, David MacKenzie, Ian Lance Taylor, Jim Meyering tee: Mike Parker, Richard M. Stallman, David MacKenzie diff --git a/NEWS b/NEWS index 73314d7..b3641ca 100644 --- a/NEWS +++ b/NEWS @@ -51,6 +51,9 @@ GNU coreutils NEWS -*- outline -*- stty allows setting the "extproc" option where supported, which is a useful setting with high latency links. + sync no longer ignores arguments, and syncs each specified file, or with the + --file-system option, the file systems associated with each specified file. + ** Changes in behavior df no longer suppresses separate exports of the same remote device, as diff --git a/doc/coreutils.texi b/doc/coreutils.texi index 4a15939..7e62bde 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -12043,18 +12043,40 @@ with @env{TZ}, libc, The GNU C Library Reference Manual}. @command{sync} writes any data buffered in memory out to disk. This can include (but is not limited to) modified superblocks, modified inodes, and delayed reads and writes. This must be implemented by the kernel; -The @command{sync} program does nothing but exercise the @code{sync} system -call. +The @command{sync} program does nothing but exercise the @code{sync}, address@hidden, @code{fsync}, and @code{fdatasync} system calls. @cindex crashes and corruption The kernel keeps data in memory to avoid doing (relatively slow) disk reads and writes. This improves performance, but if the computer crashes, data may be lost or the file system corrupted as a -result. The @command{sync} command ensures everything in memory -is written to disk. +result. The @command{sync} command instructs the kernel to write +data in memory to persistent storage. -Any arguments are ignored, except for a lone @option{--help} or address@hidden (@pxref{Common options}). +If any argument is specified then only those files will be +synchronized using the fsync(2) syscall by default. + +If at least one file is specified, it is possible to change the +synchronization method with the following options. Also see address@hidden options}. + address@hidden @samp address@hidden --data address@hidden --data +Use fdatasync(2) to sync only the data for the file, +and any metadata required to maintain file system consistency. + address@hidden --file-system address@hidden --file-system +Synchronize all the I/O waiting for the file systems that contain the file, +using the syscall syncfs(2). Note you would usually @emph{not} specify +this option if passing a device node like @samp{/dev/sda} for example, +as that would sync the containing file system rather than the referenced one. +Note also that depending on the system, passing individual device nodes or files +may have different sync characteristics than using no arguments. +I.E. arguments passed to fsync(2) may provide greater guarantees through +write barriers, than a global sync(2) used when no arguments are provided. address@hidden table @exitstatus diff --git a/m4/jm-macros.m4 b/m4/jm-macros.m4 index 58fdd97..79f124b 100644 --- a/m4/jm-macros.m4 +++ b/m4/jm-macros.m4 @@ -89,6 +89,7 @@ AC_DEFUN([coreutils_MACROS], sethostname siginterrupt sync + syncfs sysctl sysinfo tcgetpgrp diff --git a/man/sync.x b/man/sync.x index 7947bb7..6ced07e 100644 --- a/man/sync.x +++ b/man/sync.x @@ -3,4 +3,4 @@ sync \- flush file system buffers [DESCRIPTION] .\" Add any additional description here [SEE ALSO] -sync(2) +fdatasync(2), fsync(2), sync(2), syncfs(2) diff --git a/src/sync.c b/src/sync.c index e9f4d7e..80d1403 100644 --- a/src/sync.c +++ b/src/sync.c @@ -17,18 +17,42 @@ /* Written by Jim Meyering */ #include +#include #include #include #include #include "system.h" #include "error.h" -#include "long-options.h" +#include "quote.h" /* The official name of this program (e.g., no 'g' prefix). */ #define PROGRAM_NAME "sync" -#define AUTHORS proper_name ("Jim Meyering") +#define AUTHORS \ + proper_name ("Jim Meyering"), \ + proper_name ("Giuseppe Scrivano") + +#ifndef HAVE_SYNCFS +# define HAVE_SYNCFS 0 +#endif + +enum sync_mode +{ + MODE_FILE, + MODE_DATA, + MODE_FILE_SYSTEM, + MODE_SYNC +}; + +static struct option const long_options[] = +{ + {"data", no_argument, NULL, MODE_DATA}, + {"file-system", no_argument, NULL, MODE_FILE_SYSTEM}, + {GETOPT_HELP_OPTION_DECL}, + {GETOPT_VERSION_OPTION_DECL}, + {NULL, 0, NULL, 0} +}; void usage (int status) @@ -37,11 +61,20 @@ usage (int status) emit_try_help (); else { - printf (_("Usage: %s [OPTION]\n"), program_name); + printf (_("Usage: %s [OPTION] [FILE]...\n"), program_name); fputs (_("\ Force changed blocks to disk, update the super block.\n\ \n\ +If one or more file paths are specified, sync only them,\n\ +use --data and --file-system to change the default behavior\n\ +\n\ "), stdout); + + fputs (_("\ + --file-system sync the file systems that contain the files\n\ + --data only sync data for files, no unneeded metadata\n\ +"), stdout); + fputs (HELP_OPTION_DESCRIPTION, stdout); fputs (VERSION_OPTION_DESCRIPTION, stdout); emit_ancillary_info (PROGRAM_NAME); @@ -49,9 +82,82 @@ Force changed blocks to disk, update the super block.\n\ exit (status); } +/* Sync the specified FILE, or file systems associated with FILE. + Return 1 on success. */ + +static bool +sync_arg (enum sync_mode mode, char const *file) +{ + bool ret = true; + int fd; + + /* Note O_PATH is not currently supported with the sync routines. */ + if ((fd = open (file, O_RDONLY | O_NONBLOCK)) < 0 + && (fd = open (file, O_WRONLY | O_NONBLOCK)) < 0) + { + error (0, errno, _("error opening %s"), quote (file)); + return false; + } + + /* We used O_NONBLOCK above to not hang with fifos, + so reset that here. */ + int fdflags; + if ((fdflags = fcntl (fd, F_GETFL)) == -1 + || fcntl (fd, F_SETFL, fdflags & ~O_NONBLOCK) < 0) + { + error (0, errno, _("couldn't reset non-blocking mode %s"), quote (file)); + ret = false; + } + + if (ret == true) + { + int sync_status; + + switch (mode) + { + case MODE_DATA: + sync_status = fdatasync (fd); + break; + + case MODE_FILE: + sync_status = fsync (fd); + break; + +#if HAVE_SYNCFS + case MODE_FILE_SYSTEM: + sync_status = syncfs (fd); + break; +#endif + + default: + assert ("invalid sync_mode"); + } + + if (sync_status < 0) + { + error (0, errno, _("error syncing %s"), quote (file)); + ret = false; + } + } + + if (close (fd) < 0) + { + error (0, errno, _("failed to close %s"), quote (file)); + ret = false; + } + + return ret; +} + int main (int argc, char **argv) { + int c; + bool args_specified; + bool arg_data = false, arg_file_system = false; + enum sync_mode mode; + bool ok = true; + initialize_main (&argc, &argv); set_program_name (argv[0]); setlocale (LC_ALL, ""); @@ -60,14 +166,55 @@ main (int argc, char **argv) atexit (close_stdout); - parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE, Version, - usage, AUTHORS, (char const *) NULL); - if (getopt_long (argc, argv, "", NULL, NULL) != -1) - usage (EXIT_FAILURE); + while ((c = getopt_long (argc, argv, "", long_options, NULL)) + != -1) + { + switch (c) + { + case MODE_DATA: + arg_data = true; + break; + + case MODE_FILE_SYSTEM: + arg_file_system = true; + break; + + case_GETOPT_HELP_CHAR; + + case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); - if (optind < argc) - error (0, 0, _("ignoring all arguments")); + default: + usage (EXIT_FAILURE); + } + } + + args_specified = optind < argc; + + if (arg_data && arg_file_system) + { + error (EXIT_FAILURE, 0, + _("cannot specify both --data and --file-system")); + } + + if (!args_specified && arg_data) + error (EXIT_FAILURE, 0, _("--data needs at least one argument")); + + if (! args_specified || (arg_file_system && ! HAVE_SYNCFS)) + mode = MODE_SYNC; + else if (arg_file_system) + mode = MODE_FILE_SYSTEM; + else if (! arg_data) + mode = MODE_FILE; + else + mode = MODE_DATA; + + if (mode == MODE_SYNC) + sync (); + else + { + for (; optind < argc; optind++) + ok &= sync_arg (mode, argv[optind]); + } - sync (); - return EXIT_SUCCESS; + return ok ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/tests/local.mk b/tests/local.mk index 5dcbd55..bfa447c 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -375,6 +375,7 @@ all_tests = \ tests/misc/stty-row-col.sh \ tests/misc/sum.pl \ tests/misc/sum-sysv.sh \ + tests/misc/sync.sh \ tests/misc/tac.pl \ tests/misc/tac-continue.sh \ tests/misc/tac-2-nonseekable.sh \ diff --git a/tests/misc/sync.sh b/tests/misc/sync.sh new file mode 100755 index 0000000..8b0caf4 --- /dev/null +++ b/tests/misc/sync.sh @@ -0,0 +1,43 @@ +#!/bin/sh +# Test various sync(1) operations + +# Copyright (C) 2015 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 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 General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ sync + +touch file + +# fdatasync+syncfs is nonsensical +returns_ 1 sync --data --file-system || fail=1 + +# fdatasync needs an operand +returns_ 1 sync --data || fail=1 + +# Test syncing of file (fsync) (little side effects) +sync file || fail=1 + +# Ensure multiple args are processed and diagnosed +returns_ 1 sync file nofile || fail=1 + +if test "$fail" != '1'; then + # Ensure a fifo doesn't block + mkfifo_or_skip_ fifo + timeout 10 sync fifo + test $? = 124 && fail=1 +fi + +Exit $fail -- 2.1.0