Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 15 May 2015 15:49:25 +0000 (UTC)
From:      Julian Elischer <julian@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org
Subject:   svn commit: r282979 - stable/10/lib/libc/gen
Message-ID:  <201505151549.t4FFnPfS014249@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: julian
Date: Fri May 15 15:49:24 2015
New Revision: 282979
URL: https://svnweb.freebsd.org/changeset/base/282979

Log:
  MFH: r282485
    Tweak seekdir, telldir and readdir so that when htere are deletes going on,
    as seek to teh last location saved will still work. This is needed for Samba
    to be able to correctly handle delete requests from windows. This does not
    completely fix seekdir when deletes are present but fixes the worst of the
    problems. The real solution must involve some changes to the API for eh VFS
    and getdirentries(2).
  
    Obtained from:	Panzura inc
  MFH: r282550 (jhb@)
    A few style fixes and expand the comment a bit on what _fixtelldir() is
    doing.
  MFH: r282560 (jhb@)
    Tweak the comment here some more.  In particular, the previous opening
    sentence was a bit confusing.
    Noted by:	kib

Modified:
  stable/10/lib/libc/gen/directory.3
  stable/10/lib/libc/gen/readdir.c
  stable/10/lib/libc/gen/rewinddir.c
  stable/10/lib/libc/gen/telldir.c
  stable/10/lib/libc/gen/telldir.h
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/lib/libc/gen/directory.3
==============================================================================
--- stable/10/lib/libc/gen/directory.3	Fri May 15 15:36:57 2015	(r282978)
+++ stable/10/lib/libc/gen/directory.3	Fri May 15 15:49:24 2015	(r282979)
@@ -267,4 +267,27 @@ The invalidation of
 .Fn telldir
 tokens when calling
 .Fn seekdir
-is non-standard.
+is non-standard. This is a compile time option.
+.Pp
+The behaviour of
+.Fn telldir
+and
+.Fn seekdir
+is likely to be wrong if there are parallel unlinks happening
+and the directory is larger than one page.
+There is code to ensure that a
+.Fn seekdir
+to the location given by a 
+.Fn telldir
+immediately before the last 
+.Fn readdir
+will always set the correct location to return the same value as that last
+.Fn readdir
+performed.
+This is enough for some applications which want to "push back the last entry read" E.g. Samba. 
+Seeks back to any other location,
+other than the beginning of the directory,
+may result in unexpected behaviour if deletes are present.
+It is hoped that this situation will be resolved with changes to
+.Fn getdirentries
+and the VFS.

Modified: stable/10/lib/libc/gen/readdir.c
==============================================================================
--- stable/10/lib/libc/gen/readdir.c	Fri May 15 15:36:57 2015	(r282978)
+++ stable/10/lib/libc/gen/readdir.c	Fri May 15 15:49:24 2015	(r282979)
@@ -54,19 +54,25 @@ _readdir_unlocked(dirp, skip)
 	int skip;
 {
 	struct dirent *dp;
+	long initial_seek;
+	long initial_loc = 0;
 
 	for (;;) {
 		if (dirp->dd_loc >= dirp->dd_size) {
 			if (dirp->dd_flags & __DTF_READALL)
 				return (NULL);
+			initial_loc = dirp->dd_loc;
+			dirp->dd_flags &= ~__DTF_SKIPREAD;
 			dirp->dd_loc = 0;
 		}
 		if (dirp->dd_loc == 0 &&
 		    !(dirp->dd_flags & (__DTF_READALL | __DTF_SKIPREAD))) {
+			initial_seek = dirp->dd_seek;
 			dirp->dd_size = _getdirentries(dirp->dd_fd,
 			    dirp->dd_buf, dirp->dd_len, &dirp->dd_seek);
 			if (dirp->dd_size <= 0)
 				return (NULL);
+			_fixtelldir(dirp, initial_seek, initial_loc);
 		}
 		dirp->dd_flags &= ~__DTF_SKIPREAD;
 		dp = (struct dirent *)(dirp->dd_buf + dirp->dd_loc);

Modified: stable/10/lib/libc/gen/rewinddir.c
==============================================================================
--- stable/10/lib/libc/gen/rewinddir.c	Fri May 15 15:36:57 2015	(r282978)
+++ stable/10/lib/libc/gen/rewinddir.c	Fri May 15 15:49:24 2015	(r282979)
@@ -51,6 +51,7 @@ rewinddir(dirp)
 
 	if (__isthreaded)
 		_pthread_mutex_lock(&dirp->dd_lock);
+	dirp->dd_flags &= ~__DTF_SKIPREAD; /* current contents are invalid */
 	if (dirp->dd_flags & __DTF_READALL)
 		_filldir(dirp, false);
 	else {

Modified: stable/10/lib/libc/gen/telldir.c
==============================================================================
--- stable/10/lib/libc/gen/telldir.c	Fri May 15 15:36:57 2015	(r282978)
+++ stable/10/lib/libc/gen/telldir.c	Fri May 15 15:49:24 2015	(r282979)
@@ -101,9 +101,22 @@ _seekdir(dirp, loc)
 		return;
 	if (lp->loc_loc == dirp->dd_loc && lp->loc_seek == dirp->dd_seek)
 		return;
+
+	/* If it's within the same chunk of data, don't bother reloading. */
+	if (lp->loc_seek == dirp->dd_seek) {
+		/*
+		 * If we go back to 0 don't make the next readdir
+		 * trigger a call to getdirentries().
+		 */
+		if (lp->loc_loc == 0)
+			dirp->dd_flags |= __DTF_SKIPREAD;
+		dirp->dd_loc = lp->loc_loc;
+		return;
+	}
 	(void) lseek(dirp->dd_fd, (off_t)lp->loc_seek, SEEK_SET);
 	dirp->dd_seek = lp->loc_seek;
 	dirp->dd_loc = 0;
+	dirp->dd_flags &= ~__DTF_SKIPREAD; /* current contents are invalid */
 	while (dirp->dd_loc < lp->loc_loc) {
 		dp = _readdir_unlocked(dirp, 0);
 		if (dp == NULL)
@@ -112,6 +125,30 @@ _seekdir(dirp, loc)
 }
 
 /*
+ * After readdir returns the last entry in a block, a call to telldir
+ * returns a location that is after the end of that last entry.
+ * However, that location doesn't refer to a valid directory entry.
+ * Ideally, the call to telldir would return a location that refers to
+ * the first entry in the next block.  That location is not known
+ * until the next block is read, so readdir calls this function after
+ * fetching a new block to fix any such telldir locations.
+ */
+void
+_fixtelldir(DIR *dirp, long oldseek, long oldloc)
+{
+	struct ddloc *lp;
+
+	lp = LIST_FIRST(&dirp->dd_td->td_locq);
+	if (lp != NULL) {
+		if (lp->loc_loc == oldloc &&
+		    lp->loc_seek == oldseek) {
+			lp->loc_seek = dirp->dd_seek;
+			lp->loc_loc = dirp->dd_loc;
+		}
+	}
+}
+
+/*
  * Reclaim memory for telldir cookies which weren't used.
  */
 void

Modified: stable/10/lib/libc/gen/telldir.h
==============================================================================
--- stable/10/lib/libc/gen/telldir.h	Fri May 15 15:36:57 2015	(r282978)
+++ stable/10/lib/libc/gen/telldir.h	Fri May 15 15:49:24 2015	(r282979)
@@ -64,5 +64,6 @@ bool		_filldir(DIR *, bool);
 struct dirent	*_readdir_unlocked(DIR *, int);
 void 		_reclaim_telldir(DIR *);
 void 		_seekdir(DIR *, long);
+void		_fixtelldir(DIR *dirp, long oldseek, long oldloc);
 
 #endif



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201505151549.t4FFnPfS014249>