From d96c524fb3010b2a62f8489963417cef309080c4 Mon Sep 17 00:00:00 2001 From: Steve Dickson Date: Wed, 22 Jan 2014 14:11:32 -0500 Subject: [PATCH] Removed nfs-utils-1.2.10-rc1.patch Signed-off-by: Steve Dickson --- nfs-utils-1.2.10-rc1.patch | 749 ------------------------------------- 1 file changed, 749 deletions(-) delete mode 100644 nfs-utils-1.2.10-rc1.patch diff --git a/nfs-utils-1.2.10-rc1.patch b/nfs-utils-1.2.10-rc1.patch deleted file mode 100644 index 883fd50..0000000 --- a/nfs-utils-1.2.10-rc1.patch +++ /dev/null @@ -1,749 +0,0 @@ -diff --git a/support/include/nfslib.h b/support/include/nfslib.h -index f210a06..ce4b14b 100644 ---- a/support/include/nfslib.h -+++ b/support/include/nfslib.h -@@ -128,6 +128,10 @@ void fputrmtabent(FILE *fp, struct rmtabent *xep, long *pos); - void fendrmtabent(FILE *fp); - void frewindrmtabent(FILE *fp); - -+/* mydaemon */ -+void mydaemon(int nochdir, int noclose, int *pipefds); -+void release_parent(int *pipefds); -+ - /* - * wildmat borrowed from INN - */ -diff --git a/support/nfs/Makefile.am b/support/nfs/Makefile.am -index 05c2fc4..fb9b8c1 100644 ---- a/support/nfs/Makefile.am -+++ b/support/nfs/Makefile.am -@@ -2,7 +2,7 @@ - - noinst_LIBRARIES = libnfs.a - libnfs_a_SOURCES = exports.c rmtab.c xio.c rpcmisc.c rpcdispatch.c \ -- xlog.c xcommon.c wildmat.c nfsclient.c \ -+ xlog.c xcommon.c wildmat.c mydaemon.c nfsclient.c \ - nfsexport.c getfh.c nfsctl.c rpc_socket.c getport.c \ - svc_socket.c cacheio.c closeall.c nfs_mntent.c conffile.c \ - svc_create.c atomicio.c strlcpy.c strlcat.c -diff --git a/support/nfs/mydaemon.c b/support/nfs/mydaemon.c -new file mode 100644 -index 0000000..e885d60 ---- /dev/null -+++ b/support/nfs/mydaemon.c -@@ -0,0 +1,148 @@ -+/* -+ mydaemon.c -+ -+ Copyright (c) 2000 The Regents of the University of Michigan. -+ All rights reserved. -+ -+ Copyright (c) 2000 Dug Song . -+ Copyright (c) 2002 Andy Adamson . -+ Copyright (c) 2002 Marius Aamodt Eriksen . -+ Copyright (c) 2002 J. Bruce Fields . -+ Copyright (c) 2013 Jeff Layton -+ -+ All rights reserved, all wrongs reversed. -+ -+ Redistribution and use in source and binary forms, with or without -+ modification, are permitted provided that the following conditions -+ are met: -+ -+ 1. Redistributions of source code must retain the above copyright -+ notice, this list of conditions and the following disclaimer. -+ 2. Redistributions in binary form must reproduce the above copyright -+ notice, this list of conditions and the following disclaimer in the -+ documentation and/or other materials provided with the distribution. -+ 3. Neither the name of the University nor the names of its -+ contributors may be used to endorse or promote products derived -+ from this software without specific prior written permission. -+ -+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED -+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ -+*/ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/** -+ * mydaemon - daemonize, but have parent wait to exit -+ * @nochdir: skip chdir()'ing the child to / after forking if true -+ * @noclose: skip closing stdin/stdout/stderr if true -+ * @pipefds: pointer to 2 element array of pipefds -+ * -+ * This function is like daemon(), but with our own special sauce to delay -+ * the exit of the parent until the child is set up properly. A pipe is created -+ * between parent and child. The parent process will wait to exit until the -+ * child dies or writes a '1' on the pipe signaling that it started -+ * successfully. -+ */ -+void -+mydaemon(int nochdir, int noclose, int *pipefds) -+{ -+ int pid, status, tempfd; -+ -+ if (pipe(pipefds) < 0) { -+ xlog_err("mydaemon: pipe() failed: errno %d (%s)\n", -+ errno, strerror(errno)); -+ exit(1); -+ } -+ if ((pid = fork ()) < 0) { -+ xlog_err("mydaemon: fork() failed: errno %d (%s)\n", -+ errno, strerror(errno)); -+ exit(1); -+ } -+ -+ if (pid != 0) { -+ /* -+ * Parent. Wait for status from child. -+ */ -+ close(pipefds[1]); -+ if (read(pipefds[0], &status, 1) != 1) -+ exit(1); -+ exit (0); -+ } -+ /* Child. */ -+ close(pipefds[0]); -+ setsid (); -+ if (nochdir == 0) { -+ if (chdir ("/") == -1) { -+ xlog_err("mydaemon: chdir() failed: errno %d (%s)\n", -+ errno, strerror(errno)); -+ exit(1); -+ } -+ } -+ -+ while (pipefds[1] <= 2) { -+ pipefds[1] = dup(pipefds[1]); -+ if (pipefds[1] < 0) { -+ xlog_err("mydaemon: dup() failed: errno %d (%s)\n", -+ errno, strerror(errno)); -+ exit(1); -+ } -+ } -+ -+ if (noclose == 0) { -+ tempfd = open("/dev/null", O_RDWR); -+ if (tempfd >= 0) { -+ dup2(tempfd, 0); -+ dup2(tempfd, 1); -+ dup2(tempfd, 2); -+ close(tempfd); -+ } else { -+ xlog_err("mydaemon: can't open /dev/null: errno %d " -+ "(%s)\n", errno, strerror(errno)); -+ exit(1); -+ } -+ } -+ -+ return; -+} -+ -+/** -+ * release_parent - tell the parent that it can exit now -+ * @pipefds: pipefd array that was previously passed to mydaemon() -+ * -+ * This function tells the parent process of mydaemon() that it's now clear -+ * to exit(0). -+ */ -+void -+release_parent(int *pipefds) -+{ -+ int status; -+ -+ if (pipefds[1] > 0) { -+ if (write(pipefds[1], &status, 1) != 1) { -+ xlog_err("WARN: writing to parent pipe failed: errno " -+ "%d (%s)\n", errno, strerror(errno)); -+ } -+ close(pipefds[1]); -+ pipefds[1] = -1; -+ } -+} -+ -diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c -index da5fe21..00667e9 100644 ---- a/utils/exportfs/exportfs.c -+++ b/utils/exportfs/exportfs.c -@@ -27,6 +27,10 @@ - #include - #include - #include -+#include -+#include -+ -+#define INT_TO_LONG_THRESHOLD_SECS (INT_MAX - (60 * 60 * 24)) - - #include "sockaddr.h" - #include "misc.h" -@@ -406,17 +410,33 @@ unexportfs(char *arg, int verbose) - - static int can_test(void) - { -+ char buf[1024]; - int fd; - int n; -- char *setup = "nfsd 0.0.0.0 2147483647 -test-client-\n"; -+ - fd = open("/proc/net/rpc/auth.unix.ip/channel", O_WRONLY); -- if ( fd < 0) return 0; -- n = write(fd, setup, strlen(setup)); -+ if (fd < 0) -+ return 0; -+ -+ /* -+ * We introduce tolerance of 1 day to ensure that we use a -+ * LONG_MAX for the expiry timestamp before it is actually -+ * needed. To use LONG_MAX, the kernel code must have -+ * commit 2f74f972 (sunrpc: prepare NFS for 2038). -+ */ -+ if (time(NULL) > INT_TO_LONG_THRESHOLD_SECS) -+ sprintf(buf, "nfsd 0.0.0.0 %ld -test-client-\n", LONG_MAX); -+ else -+ sprintf(buf, "nfsd 0.0.0.0 %d -test-client-\n", INT_MAX); -+ -+ n = write(fd, buf, strlen(buf)); - close(fd); - if (n < 0) - return 0; -+ - fd = open("/proc/net/rpc/nfsd.export/channel", O_WRONLY); -- if ( fd < 0) return 0; -+ if (fd < 0) -+ return 0; - close(fd); - return 1; - } -diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c -index 8ee478b..fdad153 100644 ---- a/utils/gssd/gssd.c -+++ b/utils/gssd/gssd.c -@@ -54,6 +54,7 @@ - #include "err_util.h" - #include "gss_util.h" - #include "krb5_util.h" -+#include "nfslib.h" - - char pipefs_dir[PATH_MAX] = GSSD_PIPEFS_DIR; - char keytabfile[PATH_MAX] = GSSD_DEFAULT_KEYTAB_FILE; -@@ -63,6 +64,7 @@ int use_memcache = 0; - int root_uses_machine_creds = 1; - unsigned int context_timeout = 0; - char *preferred_realm = NULL; -+int pipefds[2] = { -1, -1 }; - - void - sig_die(int signal) -@@ -187,8 +189,8 @@ main(int argc, char *argv[]) - if (gssd_check_mechs() != 0) - errx(1, "Problem with gssapi library"); - -- if (!fg && daemon(0, 0) < 0) -- errx(1, "fork"); -+ if (!fg) -+ mydaemon(0, 0, pipefds); - - signal(SIGINT, sig_die); - signal(SIGTERM, sig_die); -diff --git a/utils/gssd/gssd.h b/utils/gssd/gssd.h -index 86472a1..56a18d6 100644 ---- a/utils/gssd/gssd.h -+++ b/utils/gssd/gssd.h -@@ -67,12 +67,14 @@ extern int use_memcache; - extern int root_uses_machine_creds; - extern unsigned int context_timeout; - extern char *preferred_realm; -+extern int pipefds[2]; - - TAILQ_HEAD(clnt_list_head, clnt_info) clnt_list; - - struct clnt_info { - TAILQ_ENTRY(clnt_info) list; - char *dirname; -+ char *pdir; - int dir_fd; - char *servicename; - char *servername; -diff --git a/utils/gssd/gssd_main_loop.c b/utils/gssd/gssd_main_loop.c -index ccf7fe5..9970028 100644 ---- a/utils/gssd/gssd_main_loop.c -+++ b/utils/gssd/gssd_main_loop.c -@@ -53,6 +53,7 @@ - - #include "gssd.h" - #include "err_util.h" -+#include "nfslib.h" - - extern struct pollfd *pollarray; - extern unsigned long pollsize; -@@ -245,6 +246,9 @@ gssd_run() - /* Error msg is already printed */ - exit(1); - } -+ -+ /* release the parent after the initial dir scan */ -+ release_parent(pipefds); - } - gssd_poll(pollarray, pollsize); - } -diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c -index b48d163..2a6ea97 100644 ---- a/utils/gssd/gssd_proc.c -+++ b/utils/gssd/gssd_proc.c -@@ -256,6 +256,7 @@ read_service_info(char *info_file_name, char **servicename, char **servername, - if ((nbytes = read(fd, buf, INFOBUFLEN)) == -1) - goto fail; - close(fd); -+ fd = -1; - buf[nbytes] = '\0'; - - numfields = sscanf(buf,"RPC server: %127s\n" -@@ -322,6 +323,7 @@ destroy_client(struct clnt_info *clp) - if (clp->krb5_fd != -1) close(clp->krb5_fd); - if (clp->gssd_fd != -1) close(clp->gssd_fd); - free(clp->dirname); -+ free(clp->pdir); - free(clp->servicename); - free(clp->servername); - free(clp->protocol); -@@ -403,11 +405,10 @@ process_clnt_dir_files(struct clnt_info * clp) - return -1; - snprintf(info_file_name, sizeof(info_file_name), "%s/info", - clp->dirname); -- if ((clp->servicename == NULL) && -- read_service_info(info_file_name, &clp->servicename, -- &clp->servername, &clp->prog, &clp->vers, -- &clp->protocol, (struct sockaddr *) &clp->addr)) -- return -1; -+ if (clp->prog == 0) -+ read_service_info(info_file_name, &clp->servicename, -+ &clp->servername, &clp->prog, &clp->vers, -+ &clp->protocol, (struct sockaddr *) &clp->addr); - return 0; - } - -@@ -463,6 +464,9 @@ process_clnt_dir(char *dir, char *pdir) - if (!(clp = insert_new_clnt())) - goto fail_destroy_client; - -+ if (!(clp->pdir = strdup(pdir))) -+ goto fail_destroy_client; -+ - /* An extra for the '/', and an extra for the null */ - if (!(clp->dirname = calloc(strlen(dir) + strlen(pdir) + 2, 1))) { - goto fail_destroy_client; -@@ -527,7 +531,7 @@ update_old_clients(struct dirent **namelist, int size, char *pdir) - /* only compare entries in the global list that are from the - * same pipefs parent directory as "pdir" - */ -- if (strcmp(clp->dirname, pdir) != 0) continue; -+ if (strcmp(clp->pdir, pdir) != 0) continue; - - stillhere = 0; - for (i=0; i < size; i++) { -@@ -1040,7 +1044,10 @@ process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname, - return; - default: - /* Parent: just wait on child to exit and return */ -- wait(&err); -+ do { -+ pid = wait(&err); -+ } while(pid == -1 && errno != -ECHILD); -+ - if (WIFSIGNALED(err)) - printerr(0, "WARNING: forked child was killed with signal %d\n", - WTERMSIG(err)); -@@ -1320,11 +1327,14 @@ handle_gssd_upcall(struct clnt_info *clp) - } - } - -- if (strcmp(mech, "krb5") == 0) -+ if (strcmp(mech, "krb5") == 0 && clp->servername) - process_krb5_upcall(clp, uid, clp->gssd_fd, target, service); -- else -- printerr(0, "WARNING: handle_gssd_upcall: " -- "received unknown gss mech '%s'\n", mech); -+ else { -+ if (clp->servername) -+ printerr(0, "WARNING: handle_gssd_upcall: " -+ "received unknown gss mech '%s'\n", mech); -+ do_error_downcall(clp->gssd_fd, uid, -EACCES); -+ } - - out: - free(lbuf); -diff --git a/utils/gssd/svcgssd.c b/utils/gssd/svcgssd.c -index 8aee3b2..0385725 100644 ---- a/utils/gssd/svcgssd.c -+++ b/utils/gssd/svcgssd.c -@@ -62,91 +62,7 @@ - #include "gss_util.h" - #include "err_util.h" - --/* -- * mydaemon creates a pipe between the partent and child -- * process. The parent process will wait until the -- * child dies or writes a '1' on the pipe signaling -- * that it started successfully. -- */ --int pipefds[2] = { -1, -1}; -- --static void --mydaemon(int nochdir, int noclose) --{ -- int pid, status, tempfd; -- -- if (pipe(pipefds) < 0) { -- printerr(1, "mydaemon: pipe() failed: errno %d (%s)\n", -- errno, strerror(errno)); -- exit(1); -- } -- if ((pid = fork ()) < 0) { -- printerr(1, "mydaemon: fork() failed: errno %d (%s)\n", -- errno, strerror(errno)); -- exit(1); -- } -- -- if (pid != 0) { -- /* -- * Parent. Wait for status from child. -- */ -- close(pipefds[1]); -- if (read(pipefds[0], &status, 1) != 1) -- exit(1); -- exit (0); -- } -- /* Child. */ -- close(pipefds[0]); -- setsid (); -- if (nochdir == 0) { -- if (chdir ("/") == -1) { -- printerr(1, "mydaemon: chdir() failed: errno %d (%s)\n", -- errno, strerror(errno)); -- exit(1); -- } -- } -- -- while (pipefds[1] <= 2) { -- pipefds[1] = dup(pipefds[1]); -- if (pipefds[1] < 0) { -- printerr(1, "mydaemon: dup() failed: errno %d (%s)\n", -- errno, strerror(errno)); -- exit(1); -- } -- } -- -- if (noclose == 0) { -- tempfd = open("/dev/null", O_RDWR); -- if (tempfd >= 0) { -- dup2(tempfd, 0); -- dup2(tempfd, 1); -- dup2(tempfd, 2); -- close(tempfd); -- } else { -- printerr(1, "mydaemon: can't open /dev/null: errno %d " -- "(%s)\n", errno, strerror(errno)); -- exit(1); -- } -- } -- -- return; --} -- --static void --release_parent(void) --{ -- int status; -- -- if (pipefds[1] > 0) { -- if (write(pipefds[1], &status, 1) != 1) { -- printerr(1, -- "WARN: writing to parent pipe failed: errno %d (%s)\n", -- errno, strerror(errno)); -- } -- close(pipefds[1]); -- pipefds[1] = -1; -- } --} -+static int pipefds[2] = { -1, -1 }; - - void - sig_die(int signal) -@@ -242,7 +158,7 @@ main(int argc, char *argv[]) - } - - if (!fg) -- mydaemon(0, 0); -+ mydaemon(0, 0, pipefds); - - signal(SIGINT, sig_die); - signal(SIGTERM, sig_die); -@@ -272,7 +188,7 @@ main(int argc, char *argv[]) - } - - if (!fg) -- release_parent(); -+ release_parent(pipefds); - - nfs4_init_name_mapping(NULL); /* XXX: should only do this once */ - gssd_run(); -diff --git a/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c -index b6c6231..c02849b 100644 ---- a/utils/idmapd/idmapd.c -+++ b/utils/idmapd/idmapd.c -@@ -157,9 +157,6 @@ static int nfsdopenone(struct idmap_client *); - static void nfsdreopen_one(struct idmap_client *); - static void nfsdreopen(void); - --void mydaemon(int, int); --void release_parent(void); -- - static int verbose = 0; - #define DEFAULT_IDMAP_CACHE_EXPIRY 600 /* seconds */ - static int cache_entry_expiration = 0; -@@ -167,6 +164,7 @@ static char pipefsdir[PATH_MAX]; - static char *nobodyuser, *nobodygroup; - static uid_t nobodyuid; - static gid_t nobodygid; -+static int pipefds[2] = { -1, -1 }; - - /* Used by conffile.c in libnfs.a */ - char *conf_path; -@@ -305,7 +303,7 @@ main(int argc, char **argv) - errx(1, "Unable to create name to user id mappings."); - - if (!fg) -- mydaemon(0, 0); -+ mydaemon(0, 0, pipefds); - - event_init(); - -@@ -382,7 +380,7 @@ main(int argc, char **argv) - if (nfsdret != 0 && fd == 0) - xlog_err("main: Neither NFS client nor NFSd found"); - -- release_parent(); -+ release_parent(pipefds); - - if (event_dispatch() < 0) - xlog_err("main: event_dispatch returns errno %d (%s)", -@@ -929,77 +927,3 @@ getfield(char **bpp, char *fld, size_t fldsz) - - return (0); - } --/* -- * mydaemon creates a pipe between the partent and child -- * process. The parent process will wait until the -- * child dies or writes a '1' on the pipe signaling -- * that it started successfully. -- */ --int pipefds[2] = { -1, -1}; -- --void --mydaemon(int nochdir, int noclose) --{ -- int pid, status, tempfd; -- -- if (pipe(pipefds) < 0) -- err(1, "mydaemon: pipe() failed: errno %d", errno); -- -- if ((pid = fork ()) < 0) -- err(1, "mydaemon: fork() failed: errno %d", errno); -- -- if (pid != 0) { -- /* -- * Parent. Wait for status from child. -- */ -- close(pipefds[1]); -- if (read(pipefds[0], &status, 1) != 1) -- exit(1); -- exit (0); -- } -- /* Child. */ -- close(pipefds[0]); -- setsid (); -- if (nochdir == 0) { -- if (chdir ("/") == -1) -- err(1, "mydaemon: chdir() failed: errno %d", errno); -- } -- -- while (pipefds[1] <= 2) { -- pipefds[1] = dup(pipefds[1]); -- if (pipefds[1] < 0) -- err(1, "mydaemon: dup() failed: errno %d", errno); -- } -- -- if (noclose == 0) { -- tempfd = open("/dev/null", O_RDWR); -- if (tempfd < 0) -- tempfd = open("/", O_RDONLY); -- if (tempfd >= 0) { -- dup2(tempfd, 0); -- dup2(tempfd, 1); -- dup2(tempfd, 2); -- close(tempfd); -- } else { -- err(1, "mydaemon: can't open /dev/null: errno %d", -- errno); -- exit(1); -- } -- } -- -- return; --} --void --release_parent(void) --{ -- int status; -- -- if (pipefds[1] > 0) { -- if (write(pipefds[1], &status, 1) != 1) { -- err(1, "Writing to parent pipe failed: errno %d (%s)\n", -- errno, strerror(errno)); -- } -- close(pipefds[1]); -- pipefds[1] = -1; -- } --} -diff --git a/utils/mount/network.c b/utils/mount/network.c -index e8e55a5..2853d00 100644 ---- a/utils/mount/network.c -+++ b/utils/mount/network.c -@@ -92,6 +92,9 @@ static const char *nfs_version_opttbl[] = { - "v4", - "vers", - "nfsvers", -+ "v4.0", -+ "v4.1", -+ "v4.2", - NULL, - }; - -@@ -1269,6 +1272,11 @@ nfs_nfs_version(struct mount_options *options, unsigned long *version) - progname); - return 0; - } -+ case 5: /* v4.0 */ -+ case 6: /* v4.1 */ -+ case 7: /* v4.2 */ -+ *version = 4; -+ return 1; - } - - /* -diff --git a/utils/mount/nfs.man b/utils/mount/nfs.man -index 67031b5..ecc5f64 100644 ---- a/utils/mount/nfs.man -+++ b/utils/mount/nfs.man -@@ -856,6 +856,26 @@ In the presence of multiple client network interfaces, - special routing policies, - or atypical network topologies, - the exact address to use for callbacks may be nontrivial to determine. -+.TP 1.5i -+.BR migration " / " nomigration -+Selects whether the client uses an identification string that is compatible -+with NFSv4 Transparent State Migration (TSM). -+If the mounted server supports NFSv4 migration with TSM, specify the -+.B migration -+option. -+.IP -+Some server features misbehave in the face of a migration-compatible -+identification string. -+The -+.B nomigration -+option retains the use of a traditional client indentification string -+which is compatible with legacy NFS servers. -+This is also the behavior if neither option is specified. -+A client's open and lock state cannot be migrated transparently -+when it identifies itself via a traditional identification string. -+.IP -+This mount option has no effect with NFSv4 minor versions newer than zero, -+which always use TSM-compatible client identification strings. - .SH nfs4 FILE SYSTEM TYPE - The - .BR nfs4 -@@ -1227,6 +1247,65 @@ If absolute cache coherence among clients is required, - applications should use file locking. Alternatively, applications - can also open their files with the O_DIRECT flag - to disable data caching entirely. -+.SS "File timestamp maintainence" -+NFS servers are responsible for managing file and directory timestamps -+.RB ( atime , -+.BR ctime ", and" -+.BR mtime ). -+When a file is accessed or updated on an NFS server, -+the file's timestamps are updated just like they would be on a filesystem -+local to an application. -+.P -+NFS clients cache file attributes, including timestamps. -+A file's timestamps are updated on NFS clients when its attributes -+are retrieved from the NFS server. -+Thus there may be some delay before timestamp updates -+on an NFS server appear to applications on NFS clients. -+.P -+To comply with the POSIX filesystem standard, the Linux NFS client -+relies on NFS servers to keep a file's -+.B mtime -+and -+.B ctime -+timestamps properly up to date. -+It does this by flushing local data changes to the server -+before reporting -+.B mtime -+to applications via system calls such as -+.BR stat (2). -+.P -+The Linux client handles -+.B atime -+updates more loosely, however. -+NFS clients maintain good performance by caching data, -+but that means that application reads, which normally update -+.BR atime , -+are not reflected to the server where a file's -+.B atime -+is actually maintained. -+.P -+Because of this caching behavior, -+the Linux NFS client does not support generic atime-related mount options. -+See -+.BR mount (8) -+for details on these options. -+.P -+In particular, the -+.BR atime / noatime , -+.BR diratime / nodiratime , -+.BR relatime / norelatime , -+and -+.BR strictatime / nostrictatime -+mount options have no effect on NFS mounts. -+.P -+.I /proc/mounts -+may report that the -+.B relatime -+mount option is set on NFS mounts, but in fact the -+.B atime -+semantics are always as described here, and are not like -+.B relatime -+semantics. - .SS "Directory entry caching" - The Linux NFS client caches the result of all NFS LOOKUP requests. - If the requested directory entry exists on the server,