diff options
author | Edwin Groothuis <edwin@FreeBSD.org> | 2003-12-20 12:35:43 +0000 |
---|---|---|
committer | Edwin Groothuis <edwin@FreeBSD.org> | 2003-12-20 12:35:43 +0000 |
commit | 81eba088c708db240a3ddc9d6db28a6eb6dcb961 (patch) | |
tree | 5d47e9a2de3c1c977934492f1aff568ab77e2f9a /www/mod_dav | |
parent | - Fix MASTER_SITES (diff) |
[patch] www/mod_dav - fix symlink problem
While playing with mod_dav, I found out that it didn't
follow symlinks properly. Looking through various mailinglists
I found this patch by Mark Montague (markmont@umich.edu)
I do not say we should include this patch by default, but
if a knob called WITH_SYMLINK_PATCH could be made which
adds these patches it would help people already.
PR: ports/59163
Submitted by: Edwin Groothuis <edwin@mavetju.org>
Approved by: maintainer timeout
Notes
Notes:
svn path=/head/; revision=96265
Diffstat (limited to 'www/mod_dav')
-rw-r--r-- | www/mod_dav/Makefile | 11 | ||||
-rw-r--r-- | www/mod_dav/files/SYMLINK-dav_fs_repos.c | 218 | ||||
-rw-r--r-- | www/mod_dav/files/SYMLINK-mod_dav.h | 10 |
3 files changed, 239 insertions, 0 deletions
diff --git a/www/mod_dav/Makefile b/www/mod_dav/Makefile index a6d693920574..834476276c92 100644 --- a/www/mod_dav/Makefile +++ b/www/mod_dav/Makefile @@ -7,6 +7,7 @@ PORTNAME= mod_dav PORTVERSION= 1.0.3 +PORTREVISION= 1 CATEGORIES= www MASTER_SITES= http://www.webdav.org/mod_dav/ DISTNAME= mod_dav-${PORTVERSION}-${APACHE_VERSION} @@ -32,6 +33,16 @@ CONFIGURE_ARGS= --prefix=${PREFIX} \ CONFIGURE_ENV= CFLAGS='${CFLAGS}' \ PATH="${PREFIX}/bin:${PREFIX}/sbin:${PATH}" +.if defined(WITH_SYMLINK_PATCH) +EXTRA_PATCHES= ${FILESDIR}/SYMLINK-mod_dav.h ${FILESDIR}/SYMLINK-dav_fs_repos.c +.endif + +.if !defined(WITH_SYMLINK_PATCH) +pre-everything:: + @${ECHO_MSG} "Use WITH_SYMLINK_PATCH=yes to let mod_dav follow symlinks." + @/bin/sleep 5 +.endif + post-install: ${INSTALL_DATA} ${FILESDIR}/apache.conf.mod_dav ${PREFIX}/etc/apache @${ECHO_MSG} "*******************************************************" diff --git a/www/mod_dav/files/SYMLINK-dav_fs_repos.c b/www/mod_dav/files/SYMLINK-dav_fs_repos.c new file mode 100644 index 000000000000..fa5d5384f392 --- /dev/null +++ b/www/mod_dav/files/SYMLINK-dav_fs_repos.c @@ -0,0 +1,218 @@ +--- dav_fs_repos.c.orig Mon Nov 5 16:20:32 2001 ++++ dav_fs_repos.c Tue Nov 11 14:14:11 2003 +@@ -154,6 +154,30 @@ + ** + ** PRIVATE REPOSITORY FUNCTIONS + */ ++ ++static int symlink_aware_rmdir(const char *pathname) ++{ ++ ++ struct stat finfo; ++ ++ /* Is this a symbolic link? */ ++ /* If not, just call rmdir() and return. */ ++ ++ if (lstat(pathname, &finfo) != 0) { ++ return -1; /* and errno set by lstat() */ ++ } ++ ++ if (!S_ISLNK(finfo.st_mode)) { ++ return rmdir(pathname); /* and errno set by rmdir() */ ++ } ++ ++ /* It's a symlink. */ ++ /* Remove the link itself, leaving the link's target untouched. */ ++ return unlink(pathname); /* and errno set by unlink() */ ++ ++} ++ ++ + pool *dav_fs_pool(const dav_resource *resource) + { + return resource->info->pool; +@@ -258,13 +282,25 @@ + /* chmod() the destination if the source is executable, and the + * destination already exists. */ + if ((mode & DAV_FS_MODE_XUSR) && (dst_finfo != NULL) && +- (dst_finfo->st_mode != 0)) { ++ (dst_finfo->st_mode != 0) && !S_ISLNK(dst_finfo->st_mode)) { + if (chmod(dst, mode) == -1) { + return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, + "Could not set permissions on destination"); + } + } + ++ /* If the destination is a symlink, break it so that we don't overwrite ++ * its target. */ ++ if ((dst_finfo != NULL) && S_ISLNK(dst_finfo->st_mode)) { ++ if (unlink(dst) != 0) { ++ return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, ++ "Could not break destination symlink"); ++ } ++ } ++ ++ ++ dav_set_bufsize(p, pbuf, DAV_FS_COPY_BLOCKSIZE); ++ + dav_set_bufsize(p, pbuf, DAV_FS_COPY_BLOCKSIZE); + + if ((fdi = open(src, O_RDONLY | O_BINARY)) == -1) { +@@ -565,8 +601,6 @@ + ctx->pool = r->pool; + ctx->finfo = r->finfo; + +- (void) ap_update_mtime(r, r->finfo.st_mtime); +- + /* Preserve case on OSes which fold canonical filenames */ + #if MODULE_MAGIC_NUMBER_MAJOR > 19990320 || (MODULE_MAGIC_NUMBER_MAJOR == 19990320 && MODULE_MAGIC_NUMBER_MINOR >= 8) + filename = r->case_preserved_filename; +@@ -606,9 +640,18 @@ + resource->uri = r->uri; + } + +- if (r->finfo.st_mode != 0) { ++ if (r->finfo.st_mode == 0) { ++ /* Apache says it does not exists. Is it maybe a dangling symlink? */ ++ if (lstat(ctx->pathname, &(ctx->finfo)) != 0) { ++ ctx->finfo.st_mode = 0; /* does not exist */ ++ } ++ } ++ ++ (void) ap_update_mtime(r, ctx->finfo.st_mtime); ++ ++ if (ctx->finfo.st_mode != 0) { + resource->exists = 1; +- resource->collection = S_ISDIR(r->finfo.st_mode); ++ resource->collection = S_ISDIR(ctx->finfo.st_mode); + + /* unused info in the URL will indicate a null resource */ + +@@ -979,7 +1022,7 @@ + * Note: when copying, we do not enable the postfix-traversal. + */ + /* ### we are ignoring any error here; what should we do? */ +- (void) rmdir(srcinfo->pathname); ++ (void) symlink_aware_rmdir(srcinfo->pathname); + } + else { + /* copy/move of a collection. Create the new, target collection */ +@@ -1157,6 +1200,15 @@ + } + } + ++ /* Renaming a symlink will only work in some circumstances (e.g., if ++ * old and new names are in the same directory). Be safe and break ++ * the link. ++ */ ++ if (S_ISLNK(srcinfo->finfo.st_mode)) { ++ can_rename = 0; ++ } ++ ++ + /* if we can't simply renamed, then do it the hard way... */ + if (!can_rename) { + if ((err = dav_fs_copymove_resource(1, src, dst, DAV_INFINITY, response)) == NULL) { +@@ -1226,8 +1278,31 @@ + + static dav_error * dav_fs_delete_walker(dav_walker_ctx *ctx, int calltype) + { ++ struct stat finfo; + dav_resource_private *info = ctx->resource->info; + ++ /* If this is a symlink to a directory, immediately remove the link ++ * during any "prefix traversal", then signal our caller, dav_fs_walker(), ++ * that we're done and not to follow the link to the target's children. ++ */ ++ if (ctx->resource->exists && ctx->resource->collection) { ++ if (lstat(info->pathname, &finfo) != 0) { ++ dav_add_response(ctx, ctx->resource->uri, HTTP_FORBIDDEN, NULL); ++ return NULL; /* failure */ ++ } ++ if (S_ISLNK(finfo.st_mode)) { ++ if (unlink(info->pathname) != 0) { ++ /* ### assume there is a permissions problem */ ++ /* ### use errno to generate DAV:responsedescription? */ ++ dav_add_response(ctx, ctx->resource->uri, HTTP_FORBIDDEN, NULL); ++ return NULL; /* failure */ ++ } ++ ctx->is_dir = 0; /* signal dav_fs_walker() that we're done */ ++ return NULL; /* success */ ++ } ++ } ++ ++ + /* do not attempt to remove a null resource, + * or a collection with children + */ +@@ -1237,7 +1312,7 @@ + int result; + + result = ctx->resource->collection +- ? rmdir(info->pathname) ++ ? symlink_aware_rmdir(info->pathname) + : remove(info->pathname); + + /* +@@ -1320,20 +1395,25 @@ + { + dav_error *err = NULL; + dav_walker_ctx *wctx = fsctx->wctx; +- int isdir = wctx->resource->collection; + DIR *dirp; + struct dirent *ep; + +- /* ensure the context is prepared properly, then call the func */ ++ /* ensure the context is prepared properly, then call the func. ++ * Note that if the resource is a symbolic link to a directory, ++ * and we should not recurse (for example, because we're deleting) ++ * func() will change wctx->is_dir to 0 to signal this. ++ */ ++ wctx->is_dir = wctx->resource->collection; + err = (*wctx->func)(wctx, +- isdir ++ wctx->is_dir + ? DAV_CALLTYPE_COLLECTION + : DAV_CALLTYPE_MEMBER); + if (err != NULL) { + return err; + } + +- if (!isdir) { ++ /* At this point, wctx->is_dir actually means "should we recurse?" */ ++ if (!wctx->is_dir) { + return NULL; + } + +@@ -1397,11 +1477,15 @@ + dav_buffer_place_mem(wctx->pool, + &fsctx->path1, ep->d_name, len + 1, 0); + +- if (lstat(fsctx->path1.buf, &fsctx->info1.finfo) != 0) { +- /* woah! where'd it go? */ +- /* ### should have a better error here */ +- err = dav_new_error(wctx->pool, HTTP_NOT_FOUND, 0, NULL); +- break; ++ /* stat() not lstat() because we want to follow symlinks */ ++ /* BUT, ignore dangling symlinks (symlinks whose targets don't exist) */ ++ if (stat(fsctx->path1.buf, &fsctx->info1.finfo) != 0) { ++ if (lstat(fsctx->path1.buf, &fsctx->info1.finfo) != 0) { ++ /* woah! where'd it go? */ ++ /* ### should have a better error here */ ++ err = dav_new_error(wctx->pool, HTTP_NOT_FOUND, 0, NULL); ++ break; ++ } + } + + /* copy the file to the URI, too. NOTE: we will pad an extra byte +@@ -1421,8 +1505,8 @@ + /* set up the URI for the current resource */ + fsctx->res1.uri = wctx->uri.buf; + +- /* ### for now, only process regular files (e.g. skip symlinks) */ +- if (S_ISREG(fsctx->info1.finfo.st_mode)) { ++ if (S_ISREG(fsctx->info1.finfo.st_mode) || ++ S_ISLNK(fsctx->info1.finfo.st_mode)) { + /* call the function for the specified dir + file */ + if ((err = (*wctx->func)(wctx, DAV_CALLTYPE_MEMBER)) != NULL) { + /* ### maybe add a higher-level description? */ diff --git a/www/mod_dav/files/SYMLINK-mod_dav.h b/www/mod_dav/files/SYMLINK-mod_dav.h new file mode 100644 index 000000000000..0e16170bcac0 --- /dev/null +++ b/www/mod_dav/files/SYMLINK-mod_dav.h @@ -0,0 +1,10 @@ +--- mod_dav.h.orig Tue Nov 11 14:16:48 2003 ++++ mod_dav.h Tue Nov 11 14:17:18 2003 +@@ -1431,6 +1431,7 @@ + + dav_text *propstat_404; /* (cached) propstat giving a 404 error */ + ++ int is_dir; /* state for dav_fs_walker(): should we recurse? */ + /* for COPY and MOVE operations */ + int is_move; + dav_buffer work_buf; |