Skip to content

Commit

Permalink
Allow readOnly attribute configuration on the Resources
Browse files Browse the repository at this point in the history
Use the exposed attribute value in the default and WebDAV Servlets in
addition to the existing readOnly configuration.
  • Loading branch information
rmaucher committed Dec 23, 2024
1 parent 1afe414 commit fdec436
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 17 deletions.
17 changes: 17 additions & 0 deletions java/org/apache/catalina/WebResourceRoot.java
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,23 @@ default void setCacheStrategy(CacheStrategy strategy) {
// NO-OP
}

/**
* Set if the main resources are read only.
*
* @param readOnly the value
*/
default void setReadOnly(boolean readOnly) {
// NO-OP
}

/**
* @return {@code true} if the main resources are read only, otherwise {@code false}. The default implementation
* returns {@code false}.
*/
default boolean isReadOnly() {
return false;
}

enum ResourceSetType {
PRE,
RESOURCE_JAR,
Expand Down
18 changes: 14 additions & 4 deletions java/org/apache/catalina/servlets/DefaultServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,16 @@ protected String getPathPrefix(final HttpServletRequest request) {
}


protected boolean isListings() {
return listings;
}


protected boolean isReadOnly() {
return readOnly || resources.isReadOnly();
}


@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

Expand Down Expand Up @@ -542,7 +552,7 @@ protected String determineMethodsAllowed(HttpServletRequest req) {
allow.append("OPTIONS, GET, HEAD, POST");

// PUT and DELETE depend on readonly
if (!readOnly) {
if (!isReadOnly()) {
allow.append(", PUT, DELETE");
}

Expand Down Expand Up @@ -571,7 +581,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response)
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

if (readOnly) {
if (isReadOnly()) {
sendNotAllowed(req, resp);
return;
}
Expand Down Expand Up @@ -694,7 +704,7 @@ protected File executePartialPut(HttpServletRequest req, ContentRange range, Str
@Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

if (readOnly) {
if (isReadOnly()) {
sendNotAllowed(req, resp);
return;
}
Expand Down Expand Up @@ -906,7 +916,7 @@ protected void serveResource(HttpServletRequest request, HttpServletResponse res

// Skip directory listings if we have been configured to
// suppress them
if (!listings) {
if (!isListings()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
sm.getString("defaultServlet.missingResource", request.getRequestURI()));
return;
Expand Down
20 changes: 10 additions & 10 deletions java/org/apache/catalina/servlets/WebdavServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -751,7 +751,7 @@ protected String determineMethodsAllowed(HttpServletRequest req) {
// if the resource does not exist.
StringBuilder methodsAllowed = new StringBuilder("OPTIONS, GET, POST, HEAD");

if (!readOnly) {
if (!isReadOnly()) {
methodsAllowed.append(", DELETE");
if (!resource.isDirectory()) {
methodsAllowed.append(", PUT");
Expand All @@ -765,7 +765,7 @@ protected String determineMethodsAllowed(HttpServletRequest req) {

methodsAllowed.append(", LOCK, UNLOCK, PROPPATCH, COPY, MOVE");

if (listings) {
if (isListings()) {
methodsAllowed.append(", PROPFIND");
}

Expand Down Expand Up @@ -796,7 +796,7 @@ protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throw
*/
protected void doPropfind(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

if (!listings) {
if (!isListings()) {
sendNotAllowed(req, resp);
return;
}
Expand Down Expand Up @@ -1013,7 +1013,7 @@ protected void doProppatch(HttpServletRequest req, HttpServletResponse resp) thr
return;
}

if (readOnly) {
if (isReadOnly()) {
resp.sendError(WebdavStatus.SC_FORBIDDEN);
return;
}
Expand Down Expand Up @@ -1194,7 +1194,7 @@ protected void doMkcol(HttpServletRequest req, HttpServletResponse resp) throws
return;
}

if (readOnly) {
if (isReadOnly()) {
resp.sendError(WebdavStatus.SC_FORBIDDEN);
return;
}
Expand All @@ -1221,7 +1221,7 @@ protected void doMkcol(HttpServletRequest req, HttpServletResponse resp) throws
@Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

if (readOnly) {
if (isReadOnly()) {
sendNotAllowed(req, resp);
return;
}
Expand Down Expand Up @@ -1268,7 +1268,7 @@ protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws Se
*/
protected void doCopy(HttpServletRequest req, HttpServletResponse resp) throws IOException {

if (readOnly) {
if (isReadOnly()) {
resp.sendError(WebdavStatus.SC_FORBIDDEN);
return;
}
Expand All @@ -1289,7 +1289,7 @@ protected void doCopy(HttpServletRequest req, HttpServletResponse resp) throws I
*/
protected void doMove(HttpServletRequest req, HttpServletResponse resp) throws IOException {

if (readOnly) {
if (isReadOnly()) {
resp.sendError(WebdavStatus.SC_FORBIDDEN);
return;
}
Expand Down Expand Up @@ -1318,7 +1318,7 @@ protected void doMove(HttpServletRequest req, HttpServletResponse resp) throws I
*/
protected void doLock(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

if (readOnly) {
if (isReadOnly()) {
resp.sendError(WebdavStatus.SC_FORBIDDEN);
return;
}
Expand Down Expand Up @@ -1731,7 +1731,7 @@ protected void doLock(HttpServletRequest req, HttpServletResponse resp) throws S
*/
protected void doUnlock(HttpServletRequest req, HttpServletResponse resp) throws IOException {

if (readOnly) {
if (isReadOnly()) {
resp.sendError(WebdavStatus.SC_FORBIDDEN);
return;
}
Expand Down
9 changes: 6 additions & 3 deletions java/org/apache/catalina/webresources/DirResourceSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,14 @@ public WebResource getResource(String path) {
checkPath(path);
String webAppMount = getWebAppMount();
WebResourceRoot root = getRoot();
boolean readOnly = isReadOnly();
if (path.startsWith(webAppMount)) {
/*
* Lock the path for reading until the WebResource has been constructed. The lock prevents concurrent reads
* and writes (e.g. HTTP GET and PUT / DELETE) for the same path causing corruption of the FileResource
* where some of the fields are set as if the file exists and some as set as if it does not.
*/
ResourceLock lock = lockForRead(path);
ResourceLock lock = readOnly ? null : lockForRead(path);
try {
File f = file(path.substring(webAppMount.length()), false);
if (f == null) {
Expand All @@ -119,9 +120,11 @@ public WebResource getResource(String path) {
if (f.isDirectory() && path.charAt(path.length() - 1) != '/') {
path = path + '/';
}
return new FileResource(root, path, f, isReadOnly(), getManifest(), this, lock.key);
return new FileResource(root, path, f, readOnly, getManifest(), this, readOnly ? null : lock.key);
} finally {
unlockForRead(lock);
if (!readOnly) {
unlockForRead(lock);
}
}
} else {
return new EmptyResource(root, path);
Expand Down
12 changes: 12 additions & 0 deletions java/org/apache/catalina/webresources/StandardRoot.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public class StandardRoot extends LifecycleMBeanBase implements WebResourceRoot

private Context context;
private boolean allowLinking = false;
private boolean readOnly = false;
private final List<WebResourceSet> preResources = new ArrayList<>();
private WebResourceSet main;
private final List<WebResourceSet> classResources = new ArrayList<>();
Expand Down Expand Up @@ -657,6 +658,16 @@ protected boolean isPackedWarFile() {
}


@Override
public boolean isReadOnly() {
return (readOnly || main == null || main.isReadOnly());
}

@Override
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}

// ----------------------------------------------------------- JMX Lifecycle
@Override
protected String getDomainInternal() {
Expand Down Expand Up @@ -743,6 +754,7 @@ protected WebResourceSet createMainResourceSet() {
}
if (f.isDirectory()) {
mainResourceSet = new DirResourceSet(this, "/", f.getAbsolutePath(), "/");
mainResourceSet.setReadOnly(readOnly);
} else if (f.isFile() && docBase.endsWith(".war")) {
mainResourceSet = new WarResourceSet(this, "/", f.getAbsolutePath());
} else {
Expand Down
7 changes: 7 additions & 0 deletions webapps/docs/changelog.xml
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,13 @@
Improve HTTP If headers processing according to RFC 9110. Based on pull
request <pr>796</pr> by Chenjp. (remm/markt)
</update>
<update>
Allow <code>readOnly</code> attribute configuration on the
<code>Resources</code> element and allow configure the
<code>readOnly</code> attribute value of the main resources. The
attribute value will also be used by the default and WebDAV Servlets.
(remm)
</update>
</changelog>
</subsection>
<subsection name="Coyote">
Expand Down
6 changes: 6 additions & 0 deletions webapps/docs/config/resources.xml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,12 @@
used.</p>
</attribute>

<attribute name="readOnly" required="false">
<p>If the value of this flag is <code>true</code>, then writing will
be enabled on the main resource set. The default value is
<code>false</code>.</p>
</attribute>

<attribute name="trackLockedFiles" required="false">
<p>Controls whether the track locked files feature is enabled. If
enabled, all calls to methods that return objects that lock a file and
Expand Down

0 comments on commit fdec436

Please sign in to comment.