Skip to content

Commit ba0cb38

Browse files
DmitryLitvintsevmksahakyan
authored andcommitted
quota: implement protocols specific return codes
Motivation: ---------- When quota was introduced, proper exception handling was not added to the doors other than NFS door - an oversight. As the result over quota condition tiggers generic exception handling even though some protocols provide for specific return codes in this case. Modification: ------------ Handle quota exceeded exception in all doors with the exception of DCap door. Result: ------- Protocol compliant return codes on quota exceeded exception Ticket: #7747 Patch: https://rb.dcache.org/r/14476/ Acked-by: Paul, Tigran Target: trunk Request: 11.0 Request: 10.2 Request: 10.1 Request: 10.0 Request: 9.2 Require-bool: no Require-notes: yes
1 parent b4fc440 commit ba0cb38

File tree

13 files changed

+179
-16
lines changed

13 files changed

+179
-16
lines changed

modules/dcache-chimera/src/main/java/org/dcache/chimera/namespace/ChimeraNameSpaceProvider.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import diskCacheV111.util.NotDirCacheException;
5252
import diskCacheV111.util.NotFileCacheException;
5353
import diskCacheV111.util.PermissionDeniedCacheException;
54+
import diskCacheV111.util.QuotaExceededCacheException;
5455
import diskCacheV111.util.PnfsId;
5556
import diskCacheV111.util.RetentionPolicy;
5657
import diskCacheV111.vehicles.StorageInfo;
@@ -92,6 +93,7 @@
9293
import org.dcache.chimera.DirectoryStreamB;
9394
import org.dcache.chimera.FileExistsChimeraFsException;
9495
import org.dcache.chimera.FileNotFoundChimeraFsException;
96+
import org.dcache.chimera.QuotaChimeraFsException;
9597
import org.dcache.chimera.FileState;
9698
import org.dcache.chimera.FileSystemProvider;
9799
import org.dcache.chimera.FileSystemProvider.SetXattrMode;
@@ -383,6 +385,8 @@ public FileAttributes createFile(Subject subject, String path,
383385
throw new FileNotFoundCacheException("No such directory: " + parentPath);
384386
} catch (FileExistsChimeraFsException e) {
385387
throw new FileExistsCacheException("File exists: " + path);
388+
} catch (QuotaChimeraFsException e) {
389+
throw new QuotaExceededCacheException(e.getMessage());
386390
} catch (IOException e) {
387391
throw new CacheException(CacheException.UNEXPECTED_SYSTEM_EXCEPTION,
388392
e.getMessage());

modules/dcache-ftp/src/main/java/org/dcache/ftp/door/AbstractFtpDoorV1.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
127127
import diskCacheV111.util.PermissionDeniedCacheException;
128128
import diskCacheV111.util.PnfsHandler;
129129
import diskCacheV111.util.PnfsId;
130+
import diskCacheV111.util.QuotaExceededCacheException;
130131
import diskCacheV111.util.TimeoutCacheException;
131132
import diskCacheV111.vehicles.DoorCancelledUploadNotificationMessage;
132133
import diskCacheV111.vehicles.DoorRequestInfoMessage;
@@ -3575,6 +3576,8 @@ private void store(String file, Mode mode, TransferMode xferMode,
35753576
transfer.abort(451, "Operation failed: " + e.getMessage());
35763577
} catch (PermissionDeniedCacheException e) {
35773578
transfer.abort(550, "Permission denied");
3579+
} catch (QuotaExceededCacheException e) {
3580+
transfer.abort(552, "Quota exceeded");
35783581
} catch (CacheException e) {
35793582
switch (e.getRc()) {
35803583
case CacheException.FILE_NOT_FOUND:

modules/dcache-srm/src/main/java/diskCacheV111/srm/dcache/Storage.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
116116
import diskCacheV111.util.PermissionDeniedCacheException;
117117
import diskCacheV111.util.PnfsHandler;
118118
import diskCacheV111.util.PnfsId;
119+
import diskCacheV111.util.QuotaExceededCacheException;
119120
import diskCacheV111.util.RetentionPolicy;
120121
import diskCacheV111.util.TimeoutCacheException;
121122
import diskCacheV111.vehicles.CopyManagerMessage;
@@ -1166,6 +1167,8 @@ public void putDone(SRMUser user, String localTransferPath, URI surl, boolean ov
11661167
throw new SRMException(e.getMessage(), e);
11671168
} catch (PermissionDeniedCacheException e) {
11681169
throw new SRMAuthorizationException("Permission denied.", e);
1170+
} catch (QuotaExceededCacheException e) {
1171+
throw new SRMAuthorizationException("Quota exceeded.", e);
11691172
} catch (FileExistsCacheException e) {
11701173
throw new SRMDuplicationException(surl + " exists.", e);
11711174
} catch (CacheException e) {

modules/dcache-vehicles/src/main/java/diskCacheV111/util/CacheException.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,11 @@ public class CacheException extends Exception {
188188
*/
189189
public static final int INVALID_UPDATE = 10031;
190190

191+
/**
192+
* Quota exceeded
193+
*/
194+
public static final int QUOTA_EXCEEDED = 10032;
195+
191196
/**
192197
* default error code. <b>It's recommended to use more specific error codes</b>
193198
*/
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
COPYRIGHT STATUS:
3+
Dec 1st 2001, Fermi National Accelerator Laboratory (FNAL) documents and
4+
software are sponsored by the U.S. Department of Energy under Contract No.
5+
DE-AC02-76CH03000. Therefore, the U.S. Government retains a world-wide
6+
non-exclusive, royalty-free license to publish or reproduce these documents
7+
and software for U.S. Government purposes. All documents and software
8+
available from this server are protected under the U.S. and Foreign
9+
Copyright Laws, and FNAL reserves all rights.
10+
11+
Distribution of the software available from this server is free of
12+
charge subject to the user following the terms of the Fermitools
13+
Software Legal Information.
14+
15+
Redistribution and/or modification of the software shall be accompanied
16+
by the Fermitools Software Legal Information (including the copyright
17+
notice).
18+
19+
The user is asked to feed back problems, benefits, and/or suggestions
20+
about the software to the Fermilab Software Providers.
21+
22+
Neither the name of Fermilab, the URA, nor the names of the contributors
23+
may be used to endorse or promote products derived from this software
24+
without specific prior written permission.
25+
26+
DISCLAIMER OF LIABILITY (BSD):
27+
28+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
31+
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FERMILAB,
32+
OR THE URA, OR THE U.S. DEPARTMENT of ENERGY, OR CONTRIBUTORS BE LIABLE
33+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
34+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
35+
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
36+
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
37+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
38+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
39+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40+
41+
Liabilities of the Government:
42+
43+
This software is provided by URA, independent from its Prime Contract
44+
with the U.S. Department of Energy. URA is acting independently from
45+
the Government and in its own private capacity and is not acting on
46+
behalf of the U.S. Government, nor as its contractor nor its agent.
47+
Correspondingly, it is understood and agreed that the U.S. Government
48+
has no connection to this software and in no manner whatsoever shall
49+
be liable for nor assume any responsibility or obligation for any claim,
50+
cost, or damages arising out of or resulting from the use of the software
51+
available from this server.
52+
53+
Export Control:
54+
55+
All documents and software available from this server are subject to U.S.
56+
export control laws. Anyone downloading information from this server is
57+
obligated to secure any necessary Government licenses before exporting
58+
documents or software obtained from this server.
59+
*/
60+
61+
package diskCacheV111.util;
62+
63+
public class QuotaExceededCacheException extends CacheException {
64+
65+
private static final long serialVersionUID = -1L;
66+
67+
public QuotaExceededCacheException(String msg) {
68+
super(CacheException.QUOTA_EXCEEDED, msg);
69+
}
70+
71+
public QuotaExceededCacheException(String msg, Throwable cause) {
72+
super(CacheException.QUOTA_EXCEEDED, msg, cause);
73+
}
74+
}

modules/dcache-webdav/src/main/java/org/dcache/webdav/DcacheResourceFactory.java

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import static com.google.common.base.Preconditions.checkState;
55
import static com.google.common.collect.Iterables.cycle;
66
import static com.google.common.collect.Iterables.limit;
7+
import static io.milton.http.quota.StorageChecker.StorageErrorReason.SER_DISK_FULL;
8+
import static io.milton.http.quota.StorageChecker.StorageErrorReason.SER_QUOTA_EXCEEDED;
79
import static java.util.Arrays.asList;
810
import static java.util.Objects.requireNonNull;
911
import static java.util.concurrent.TimeUnit.MILLISECONDS;
@@ -50,6 +52,7 @@
5052
import diskCacheV111.util.PermissionDeniedCacheException;
5153
import diskCacheV111.util.PnfsHandler;
5254
import diskCacheV111.util.PnfsId;
55+
import diskCacheV111.util.QuotaExceededCacheException;
5356
import diskCacheV111.util.TimeoutCacheException;
5457
import diskCacheV111.vehicles.DoorRequestInfoMessage;
5558
import diskCacheV111.vehicles.DoorTransferFinishedMessage;
@@ -735,10 +738,9 @@ && isImpatientClientProxied()
735738
*/
736739
public DcacheResource createFile(FsPath path, InputStream inputStream, Long length)
737740
throws CacheException, InterruptedException, IOException,
738-
URISyntaxException, BadRequestException {
741+
URISyntaxException, BadRequestException {
739742
Subject subject = getSubject();
740743
Restriction restriction = getRestriction();
741-
742744
checkUploadSize(length);
743745

744746
WriteTransfer transfer = new WriteTransfer(_pnfs, subject, restriction, path);
@@ -802,6 +804,11 @@ public DcacheResource createFile(FsPath path, InputStream inputStream, Long leng
802804
transfer.deleteNameSpaceEntry();
803805
}
804806
}
807+
} catch (QuotaExceededCacheException e) {
808+
throw new InsufficientStorageException(e.getMessage(),
809+
null,
810+
SER_QUOTA_EXCEEDED);
811+
805812
} finally {
806813
_transfers.remove((int) transfer.getId());
807814
}
@@ -811,7 +818,7 @@ public DcacheResource createFile(FsPath path, InputStream inputStream, Long leng
811818

812819
public String getWriteUrl(FsPath path, Long length)
813820
throws CacheException, InterruptedException,
814-
URISyntaxException {
821+
URISyntaxException {
815822
Subject subject = getSubject();
816823
Restriction restriction = getRestriction();
817824

@@ -853,6 +860,10 @@ public String getWriteUrl(FsPath path, Long length)
853860
transfer.deleteNameSpaceEntry();
854861
}
855862
}
863+
} catch (QuotaExceededCacheException e) {
864+
throw new InsufficientStorageException(e.getMessage(),
865+
null,
866+
SER_QUOTA_EXCEEDED);
856867
} finally {
857868
if (uri == null) {
858869
_transfers.remove((int) transfer.getId());
@@ -1437,7 +1448,9 @@ private OptionalLong getMaxUploadSize() {
14371448
private void checkUploadSize(Long length) {
14381449
OptionalLong maxUploadSize = getMaxUploadSize();
14391450
checkStorageSufficient(!maxUploadSize.isPresent() || length == null
1440-
|| length <= maxUploadSize.getAsLong(), "Upload too large");
1451+
|| length <= maxUploadSize.getAsLong(),
1452+
SER_DISK_FULL,
1453+
"Upload too large");
14411454
}
14421455

14431456
private boolean isAdmin() {
@@ -1944,7 +1957,8 @@ public void relayData(InputStream inputStream)
19441957
throw new BadRequestException(connection.getResponseMessage());
19451958
case 507: // Insufficient Storage
19461959
throw new InsufficientStorageException(connection.getResponseMessage(),
1947-
null);
1960+
null,
1961+
SER_DISK_FULL);
19481962
case ResponseStatus.SC_INTERNAL_SERVER_ERROR:
19491963
throw new CacheException(
19501964
"Pool error: " + connection.getResponseMessage());

modules/dcache-webdav/src/main/java/org/dcache/webdav/DcacheStandardFilter.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,9 @@ public void process(FilterChain chain, Request request, Response response) {
7575
LOGGER.debug("Client supplied bad request parameters: {}", e.getMessage());
7676
responseHandler.respondBadRequest(e.getResource(), response, request);
7777
} catch (InsufficientStorageException e) {
78-
responseHandler.respondInsufficientStorage(request, response,
79-
StorageChecker.StorageErrorReason.SER_DISK_FULL);
78+
responseHandler.respondInsufficientStorage(request,
79+
response,
80+
e.getReason());
8081
} catch (ConflictException e) {
8182
responseHandler.respondConflict(e.getResource(), response, request, e.getMessage());
8283
} catch (NotAuthorizedException e) {
Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package org.dcache.webdav;
22

3+
import static java.util.Objects.requireNonNull;
34
import static org.dcache.util.Exceptions.genericCheck;
45

6+
import io.milton.http.quota.StorageChecker;
57
import io.milton.resource.Resource;
68

79
/**
@@ -10,16 +12,32 @@
1012
*/
1113
public class InsufficientStorageException extends WebDavException {
1214

13-
public static void checkStorageSufficient(boolean isOK, String template, Object... arguments)
15+
private final StorageChecker.StorageErrorReason reason;
16+
17+
public static void checkStorageSufficient(boolean isOK,
18+
StorageChecker.StorageErrorReason reason,
19+
String template,
20+
Object... arguments)
1421
throws InsufficientStorageException {
15-
genericCheck(isOK, s -> new InsufficientStorageException(s, null), template, arguments);
22+
genericCheck(isOK, s -> new InsufficientStorageException(s, null, reason), template, arguments);
23+
}
24+
25+
public StorageChecker.StorageErrorReason getReason() {
26+
return reason;
1627
}
1728

18-
public InsufficientStorageException(String message, Resource resource) {
29+
public InsufficientStorageException(String message,
30+
Resource resource,
31+
StorageChecker.StorageErrorReason reason) {
1932
super(message, resource);
33+
this.reason = requireNonNull(reason);
2034
}
2135

22-
public InsufficientStorageException(String message, Throwable cause, Resource resource) {
36+
public InsufficientStorageException(String message,
37+
Throwable cause,
38+
Resource resource,
39+
StorageChecker.StorageErrorReason reason) {
2340
super(message, cause, resource);
41+
this.reason = requireNonNull(reason);
2442
}
2543
}

modules/dcache-webdav/src/main/java/org/dcache/webdav/WebDavException.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package org.dcache.webdav;
22

3+
import static io.milton.http.quota.StorageChecker.StorageErrorReason.SER_DISK_FULL;
4+
import static io.milton.http.quota.StorageChecker.StorageErrorReason.SER_QUOTA_EXCEEDED;
35
import com.google.common.collect.ImmutableSet;
46
import diskCacheV111.util.CacheException;
57
import diskCacheV111.util.PermissionDeniedCacheException;
8+
import diskCacheV111.util.QuotaExceededCacheException;
69
import io.milton.resource.Resource;
710
import javax.annotation.Nonnull;
811

@@ -59,17 +62,22 @@ public static WebDavException of(@Nonnull CacheException e, Resource resource)
5962
{
6063
if (e instanceof PermissionDeniedCacheException) {
6164
return WebDavExceptions.permissionDenied(resource);
65+
} else if (e instanceof QuotaExceededCacheException) {
66+
return new InsufficientStorageException(e.getMessage(),
67+
e,
68+
resource,
69+
SER_QUOTA_EXCEEDED);
6270
}
6371

6472
switch (e.getRc()) {
6573
case 192: // Pool-to-pool required, but destination cost exceeded.
6674
case 194: // Pool-to-pool required, but source cost exceeded.
6775
return new InsufficientStorageException("Unable to ready file for access",
68-
e, resource);
76+
e, resource, SER_DISK_FULL);
6977
}
7078

7179
if (FULL_POOL_MESSAGE.contains(e.getMessage())) {
72-
return new InsufficientStorageException(e.getMessage(), e, resource);
80+
return new InsufficientStorageException(e.getMessage(), e, resource, SER_DISK_FULL);
7381
}
7482

7583
return new WebDavException(e.getMessage(), e, resource);

modules/dcache-webdav/src/main/java/org/dcache/webdav/transfer/CopyFilter.java

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,14 @@
3838
import io.milton.servlet.ServletResponse;
3939
import java.net.URI;
4040
import java.net.URISyntaxException;
41+
import java.nio.charset.StandardCharsets;
4142
import java.security.AccessController;
4243
import java.security.cert.X509Certificate;
4344
import java.util.Collections;
4445
import java.util.EnumSet;
4546
import java.util.HashMap;
4647
import java.util.HashSet;
48+
import java.io.IOException;
4749
import java.util.List;
4850
import java.util.Map;
4951
import java.util.Objects;
@@ -268,16 +270,36 @@ public void validateOidcClientParameters() {
268270
}
269271

270272
@Override
271-
public void process(FilterChain chain, Request request, Response response) {
273+
public void process(FilterChain chain, Request request, Response response ) {
272274
try {
273275
if (isRequestThirdPartyCopy(request)) {
274276
processThirdPartyCopy(request, response);
275277
} else {
276278
chain.process(request, response);
277279
}
278280
} catch (ErrorResponseException e) {
279-
ServletResponse.getResponse().setStatus(e.getStatus().code,
280-
e.getMessage());
281+
var r = ServletResponse.getResponse();
282+
int code = e.getStatus().code;
283+
r.setStatus(code, e.getMessage());
284+
if (code == 507) {
285+
/**
286+
* https://www.rfc-editor.org/rfc/rfc4331.html#section-6
287+
* stipulates that insufficient storage response error
288+
* must be accompanied by the following error response:
289+
*/
290+
r.setContentType("application/xml");
291+
r.setCharacterEncoding("UTF-8");
292+
var body = "<?xml version=\"1.0\">\n<error xmlns=\"DAV:\">\n<quota-not-exceeded/>\n</error>\n";
293+
int len = StandardCharsets.UTF_8.encode(body).limit();
294+
r.setContentLength(len);
295+
try {
296+
var out = r.getWriter();
297+
out.write(body);
298+
} catch (IOException ioe) {
299+
LOGGER.warn("Failed to write error response body: {}",
300+
ioe.toString());
301+
}
302+
}
281303
} catch (BadRequestException e) {
282304
ServletResponse.getResponse().setStatus(HttpServletResponse.SC_BAD_REQUEST,
283305
e.getMessage());

0 commit comments

Comments
 (0)