diff options
Diffstat (limited to 'io')
-rw-r--r-- | io/Makefile | 2 | ||||
-rw-r--r-- | io/Versions | 4 | ||||
-rw-r--r-- | io/file_change_detection.c | 118 | ||||
-rw-r--r-- | io/tst-file_change_detection.c | 57 |
4 files changed, 150 insertions, 31 deletions
diff --git a/io/Makefile b/io/Makefile index 04c4647dc0..cf380f3516 100644 --- a/io/Makefile +++ b/io/Makefile @@ -55,7 +55,7 @@ routines := \ posix_fadvise posix_fadvise64 \ posix_fallocate posix_fallocate64 \ sendfile sendfile64 copy_file_range \ - utimensat futimens + utimensat futimens file_change_detection # These routines will be omitted from the libc shared object. # Instead the static object files will be included in a special archive diff --git a/io/Versions b/io/Versions index f7e5dbe49e..ee468055ff 100644 --- a/io/Versions +++ b/io/Versions @@ -137,5 +137,9 @@ libc { __fcntl_nocancel; __open64_nocancel; __write_nocancel; + __file_is_unchanged; + __file_change_detection_for_stat; + __file_change_detection_for_path; + __file_change_detection_for_fp; } } diff --git a/io/file_change_detection.c b/io/file_change_detection.c new file mode 100644 index 0000000000..c6d700ed05 --- /dev/null +++ b/io/file_change_detection.c @@ -0,0 +1,118 @@ +/* Detecting file changes using modification times. + Copyright (C) 2017-2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + +#include <file_change_detection.h> + +#include <errno.h> +#include <stddef.h> + +bool +__file_is_unchanged (const struct file_change_detection *left, + const struct file_change_detection *right) +{ + if (left->size < 0 || right->size < 0) + /* Negative sizes are used as markers and never match. */ + return false; + else if (left->size == 0 && right->size == 0) + /* Both files are empty or do not exist, so they have the same + content, no matter what the other fields indicate. */ + return true; + else + return left->size == right->size + && left->ino == right->ino + && left->mtime.tv_sec == right->mtime.tv_sec + && left->mtime.tv_nsec == right->mtime.tv_nsec + && left->ctime.tv_sec == right->ctime.tv_sec + && left->ctime.tv_nsec == right->ctime.tv_nsec; +} +libc_hidden_def (__file_is_unchanged) + +void +__file_change_detection_for_stat (struct file_change_detection *file, + const struct stat64 *st) +{ + if (S_ISDIR (st->st_mode)) + /* Treat as empty file. */ + file->size = 0; + else if (!S_ISREG (st->st_mode)) + /* Non-regular files cannot be cached. */ + file->size = -1; + else + { + file->size = st->st_size; + file->ino = st->st_ino; + file->mtime = st->st_mtim; + file->ctime = st->st_ctim; + } +} +libc_hidden_def (__file_change_detection_for_stat) + +bool +__file_change_detection_for_path (struct file_change_detection *file, + const char *path) +{ + struct stat64 st; + if (stat64 (path, &st) != 0) + switch (errno) + { + case EACCES: + case EISDIR: + case ELOOP: + case ENOENT: + case ENOTDIR: + case EPERM: + /* Ignore errors due to file system contents. Instead, treat + the file as empty. */ + file->size = 0; + return true; + default: + /* Other errors are fatal. */ + return false; + } + else /* stat64 was successfull. */ + { + __file_change_detection_for_stat (file, &st); + return true; + } +} +libc_hidden_def (__file_change_detection_for_path) + +bool +__file_change_detection_for_fp (struct file_change_detection *file, + FILE *fp) +{ + if (fp == NULL) + { + /* The file does not exist. */ + file->size = 0; + return true; + } + else + { + struct stat64 st; + if (fstat64 (__fileno (fp), &st) != 0) + /* If we already have a file descriptor, all errors are fatal. */ + return false; + else + { + __file_change_detection_for_stat (file, &st); + return true; + } + } +} +libc_hidden_def (__file_change_detection_for_fp) diff --git a/io/tst-file_change_detection.c b/io/tst-file_change_detection.c index 035dd39c4d..6e00e787b1 100644 --- a/io/tst-file_change_detection.c +++ b/io/tst-file_change_detection.c @@ -16,10 +16,6 @@ License along with the GNU C Library; if not, see <https://www.gnu.org/licenses/>. */ -/* The header uses the internal __fileno symbol, which is not - available outside of libc (even to internal tests). */ -#define __fileno(fp) fileno (fp) - #include <file_change_detection.h> #include <array_length.h> @@ -40,7 +36,7 @@ all_same (struct file_change_detection *array, size_t length) { if (test_verbose > 0) printf ("info: comparing %zu and %zu\n", i, j); - TEST_VERIFY (file_is_unchanged (array + i, array + j)); + TEST_VERIFY (__file_is_unchanged (array + i, array + j)); } } @@ -54,7 +50,7 @@ all_different (struct file_change_detection *array, size_t length) continue; if (test_verbose > 0) printf ("info: comparing %zu and %zu\n", i, j); - TEST_VERIFY (!file_is_unchanged (array + i, array + j)); + TEST_VERIFY (!__file_is_unchanged (array + i, array + j)); } } @@ -105,24 +101,24 @@ do_test (void) struct file_change_detection fcd[10]; int i = 0; /* Two empty files always have the same contents. */ - TEST_VERIFY (file_change_detection_for_path (&fcd[i++], path_empty1)); - TEST_VERIFY (file_change_detection_for_path (&fcd[i++], path_empty2)); + TEST_VERIFY (__file_change_detection_for_path (&fcd[i++], path_empty1)); + TEST_VERIFY (__file_change_detection_for_path (&fcd[i++], path_empty2)); /* So does a missing file (which is treated as empty). */ - TEST_VERIFY (file_change_detection_for_path (&fcd[i++], - path_does_not_exist)); + TEST_VERIFY (__file_change_detection_for_path (&fcd[i++], + path_does_not_exist)); /* And a symbolic link loop. */ - TEST_VERIFY (file_change_detection_for_path (&fcd[i++], path_loop)); + TEST_VERIFY (__file_change_detection_for_path (&fcd[i++], path_loop)); /* And a dangling symbolic link. */ - TEST_VERIFY (file_change_detection_for_path (&fcd[i++], path_dangling)); + TEST_VERIFY (__file_change_detection_for_path (&fcd[i++], path_dangling)); /* And a directory. */ - TEST_VERIFY (file_change_detection_for_path (&fcd[i++], tempdir)); + TEST_VERIFY (__file_change_detection_for_path (&fcd[i++], tempdir)); /* And a symbolic link to an empty file. */ - TEST_VERIFY (file_change_detection_for_path (&fcd[i++], path_to_empty1)); + TEST_VERIFY (__file_change_detection_for_path (&fcd[i++], path_to_empty1)); /* Likewise for access the file via a FILE *. */ - TEST_VERIFY (file_change_detection_for_fp (&fcd[i++], fp_empty1)); - TEST_VERIFY (file_change_detection_for_fp (&fcd[i++], fp_empty2)); + TEST_VERIFY (__file_change_detection_for_fp (&fcd[i++], fp_empty1)); + TEST_VERIFY (__file_change_detection_for_fp (&fcd[i++], fp_empty2)); /* And a NULL FILE * (missing file). */ - TEST_VERIFY (file_change_detection_for_fp (&fcd[i++], NULL)); + TEST_VERIFY (__file_change_detection_for_fp (&fcd[i++], NULL)); TEST_COMPARE (i, array_length (fcd)); all_same (fcd, array_length (fcd)); @@ -132,9 +128,9 @@ do_test (void) { struct file_change_detection fcd[3]; int i = 0; - TEST_VERIFY (file_change_detection_for_path (&fcd[i++], path_file1)); - TEST_VERIFY (file_change_detection_for_path (&fcd[i++], path_to_file1)); - TEST_VERIFY (file_change_detection_for_fp (&fcd[i++], fp_file1)); + TEST_VERIFY (__file_change_detection_for_path (&fcd[i++], path_file1)); + TEST_VERIFY (__file_change_detection_for_path (&fcd[i++], path_to_file1)); + TEST_VERIFY (__file_change_detection_for_fp (&fcd[i++], fp_file1)); TEST_COMPARE (i, array_length (fcd)); all_same (fcd, array_length (fcd)); } @@ -144,20 +140,20 @@ do_test (void) struct file_change_detection fcd[5]; int i = 0; /* The other files are not empty. */ - TEST_VERIFY (file_change_detection_for_path (&fcd[i++], path_empty1)); + TEST_VERIFY (__file_change_detection_for_path (&fcd[i++], path_empty1)); /* These two files have the same contents, but have different file identity. */ - TEST_VERIFY (file_change_detection_for_path (&fcd[i++], path_file1)); - TEST_VERIFY (file_change_detection_for_path (&fcd[i++], path_file2)); + TEST_VERIFY (__file_change_detection_for_path (&fcd[i++], path_file1)); + TEST_VERIFY (__file_change_detection_for_path (&fcd[i++], path_file2)); /* FIFOs are always different, even with themselves. */ - TEST_VERIFY (file_change_detection_for_path (&fcd[i++], path_fifo)); - TEST_VERIFY (file_change_detection_for_path (&fcd[i++], path_fifo)); + TEST_VERIFY (__file_change_detection_for_path (&fcd[i++], path_fifo)); + TEST_VERIFY (__file_change_detection_for_path (&fcd[i++], path_fifo)); TEST_COMPARE (i, array_length (fcd)); all_different (fcd, array_length (fcd)); /* Replacing the file with its symbolic link does not make a difference. */ - TEST_VERIFY (file_change_detection_for_path (&fcd[1], path_to_file1)); + TEST_VERIFY (__file_change_detection_for_path (&fcd[1], path_to_file1)); all_different (fcd, array_length (fcd)); } @@ -166,16 +162,17 @@ do_test (void) for (int use_stdio = 0; use_stdio < 2; ++use_stdio) { struct file_change_detection initial; - TEST_VERIFY (file_change_detection_for_path (&initial, path_file1)); + TEST_VERIFY (__file_change_detection_for_path (&initial, path_file1)); while (true) { support_write_file_string (path_file1, "line\n"); struct file_change_detection current; if (use_stdio) - TEST_VERIFY (file_change_detection_for_fp (¤t, fp_file1)); + TEST_VERIFY (__file_change_detection_for_fp (¤t, fp_file1)); else - TEST_VERIFY (file_change_detection_for_path (¤t, path_file1)); - if (!file_is_unchanged (&initial, ¤t)) + TEST_VERIFY (__file_change_detection_for_path + (¤t, path_file1)); + if (!__file_is_unchanged (&initial, ¤t)) break; /* Wait for a bit to reduce system load. */ usleep (100 * 1000); |