From owner-svn-src-all@FreeBSD.ORG Tue May 5 14:52:35 2015 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 63A98330; Tue, 5 May 2015 14:52:35 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 448B61D8C; Tue, 5 May 2015 14:52:35 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.9/8.14.9) with ESMTP id t45EqZDF027619; Tue, 5 May 2015 14:52:35 GMT (envelope-from julian@FreeBSD.org) Received: (from julian@localhost) by svn.freebsd.org (8.14.9/8.14.9/Submit) id t45EqXXv027613; Tue, 5 May 2015 14:52:33 GMT (envelope-from julian@FreeBSD.org) Message-Id: <201505051452.t45EqXXv027613@svn.freebsd.org> X-Authentication-Warning: svn.freebsd.org: julian set sender to julian@FreeBSD.org using -f From: Julian Elischer Date: Tue, 5 May 2015 14:52:33 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r282485 - head/lib/libc/gen X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 05 May 2015 14:52:35 -0000 Author: julian Date: Tue May 5 14:52:33 2015 New Revision: 282485 URL: https://svnweb.freebsd.org/changeset/base/282485 Log: 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 MFC after: 1 week Modified: head/lib/libc/gen/directory.3 head/lib/libc/gen/readdir.c head/lib/libc/gen/rewinddir.c head/lib/libc/gen/telldir.c head/lib/libc/gen/telldir.h Modified: head/lib/libc/gen/directory.3 ============================================================================== --- head/lib/libc/gen/directory.3 Tue May 5 14:19:22 2015 (r282484) +++ head/lib/libc/gen/directory.3 Tue May 5 14:52:33 2015 (r282485) @@ -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: head/lib/libc/gen/readdir.c ============================================================================== --- head/lib/libc/gen/readdir.c Tue May 5 14:19:22 2015 (r282484) +++ head/lib/libc/gen/readdir.c Tue May 5 14:52:33 2015 (r282485) @@ -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: head/lib/libc/gen/rewinddir.c ============================================================================== --- head/lib/libc/gen/rewinddir.c Tue May 5 14:19:22 2015 (r282484) +++ head/lib/libc/gen/rewinddir.c Tue May 5 14:52:33 2015 (r282485) @@ -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: head/lib/libc/gen/telldir.c ============================================================================== --- head/lib/libc/gen/telldir.c Tue May 5 14:19:22 2015 (r282484) +++ head/lib/libc/gen/telldir.c Tue May 5 14:52:33 2015 (r282485) @@ -101,9 +101,21 @@ _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 +124,27 @@ _seekdir(dirp, loc) } /* + * when we do a read and cross a boundary, any telldir we + * just did will have wrong information in it. + * We need to move it from "beyond the end of the previous chunk" + * to "the beginning of the new chunk" + */ +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: head/lib/libc/gen/telldir.h ============================================================================== --- head/lib/libc/gen/telldir.h Tue May 5 14:19:22 2015 (r282484) +++ head/lib/libc/gen/telldir.h Tue May 5 14:52:33 2015 (r282485) @@ -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