Skip to content

Commit

Permalink
nfs: support ability to set file checksum(s) via dot command
Browse files Browse the repository at this point in the history
Motivation:

Some admin scripts (such as client to enstore) will need this
ability in the near future.

Modification:

Add the operation type to PSET.  Support overwrite and multiple
sets only if user is ROOT.

Result:

Requirement satisfied.

Target: master
Requires-book: yes
Requires-notes: yes
Patch: https://rb.dcache.org/r/11923
Acked-by: Tigran
  • Loading branch information
alrossi committed Sep 6, 2019
1 parent fbed8d5 commit 0fb511c
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 27 deletions.
41 changes: 41 additions & 0 deletions docs/TheBook/src/main/markdown/rf-dot-commands.md
Expand Up @@ -68,6 +68,47 @@ A comma-delimited list of `type:value` pairs for all checksums stored in the dat
$ cat ".(get)(test_file-Thu_Oct_23_10:39:37_CDT_2014-109)(checksums)"
$ ADLER32:66300001

## SET CHECKSUM

Set a checksum value for a file. An ordinary user can only set one
checksum (type, value) pair, and cannot overwrite existing values. A ROOT
user is allowed to set multiple types (successively) and also to overwrite
values for any type.

### USAGE:

touch ".(fset)(<filename>)(checksum)(<type>)(<value>)"

##### EXAMPLES:

[arossi@otfrid volatile]$ touch testfile

[arossi@otfrid volatile]$ cat ".(get)(testfile)(checksum)"

[arossi@otfrid volatile]$ touch ".(fset)(testfile)(checksum)(ADLER32)(ffffffff)"

[arossi@otfrid volatile]$ cat ".(get)(testfile)(checksum)"
ADLER32:ffffffff

[arossi@otfrid volatile]$ touch ".(fset)(testfile)(checksum)(ADLER32)(fffffff0)"
touch: setting times of ‘.(fset)(testfile)(checksum)(ADLER32)(fffffff0)’: Operation not permitted

[arossi@otfrid volatile]$ ksu
Authenticated arossi@FNAL.GOV
Account root: authorization for arossi@FNAL.GOV successful
Changing uid to root (0)

[root@otfrid volatile]# touch ".(fset)(testfile)(checksum)(ADLER32)(fffffff0)"

[root@otfrid volatile]# cat ".(get)(testfile)(checksum)"
ADLER32:fffffff0

[root@otfrid volatile]# touch ".(fset)(testfile)(checksum)(MD5)(12341234123412345678567856785678)"

[root@otfrid volatile]# cat ".(get)(testfile)(checksum)"
ADLER32:fffffff0, MD5:12341234123412345678567856785678


## GET PIN(S)

Get pins on a given file.
Expand Down
38 changes: 17 additions & 21 deletions modules/chimera/src/main/java/org/dcache/chimera/FsInode_PCRC.java
Expand Up @@ -28,33 +28,29 @@
* @author arossi
*/
public class FsInode_PCRC extends FsInode_PGET {
private String _checksum;

public FsInode_PCRC(FileSystemProvider fs, long ino) {
super(fs, ino, FsInodeType.PCRC);
}

protected String value() throws ChimeraFsException {
if (_checksum == null) {
Set<Checksum> results = _fs.getInodeChecksums(this);
StringBuilder sb = new StringBuilder();

Iterator<Checksum> it = results.iterator();
if (it.hasNext()) {
Checksum result = it.next();
sb.append(result.getType()).append(':').append(
result.getValue());
}

while (it.hasNext()) {
Checksum result = it.next();
sb.append(", ").append(result.getType()).append(':').append(
result.getValue());
}

sb.append(NEWLINE);
_checksum = sb.toString();
Set<Checksum> results = _fs.getInodeChecksums(this);
StringBuilder sb = new StringBuilder();

Iterator<Checksum> it = results.iterator();
if (it.hasNext()) {
Checksum result = it.next();
sb.append(result.getType()).append(':').append(
result.getValue());
}
return _checksum;

while (it.hasNext()) {
Checksum result = it.next();
sb.append(", ").append(result.getType()).append(':').append(
result.getValue());
}

sb.append(NEWLINE);
return sb.toString();
}
}
53 changes: 47 additions & 6 deletions modules/chimera/src/main/java/org/dcache/chimera/FsInode_PSET.java
Expand Up @@ -19,16 +19,20 @@
import com.google.common.base.Charsets;

import java.util.Arrays;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.dcache.chimera.posix.Stat;
import org.dcache.util.Checksum;
import org.dcache.util.ChecksumType;

public class FsInode_PSET extends FsInode {
private static final String SIZE = "size";
private static final String IO = "io";
private static final String ONLN = "bringonline";
private static final String STG = "stage";
private static final String PIN = "pin";
private static final String SIZE = "size";
private static final String IO = "io";
private static final String ONLN = "bringonline";
private static final String STG = "stage";
private static final String PIN = "pin";
private static final String CKS = "checksum";

private final String[] _args;

Expand All @@ -42,6 +46,8 @@ public boolean isDirectory() {
return false;
}

public boolean isChecksum() { return CKS.equals(_args[0]); }

@Override
public int read(long pos, byte[] data, int offset, int len) {
return -1;
Expand Down Expand Up @@ -70,10 +76,12 @@ public void setStat(Stat newStat) throws ChimeraFsException
case PIN:
handlePinRequest();
break;
case CKS:
handleSetChecksum();
break;
default:
break;
}

}
}

Expand Down Expand Up @@ -152,4 +160,37 @@ private void handlePinRequest() throws ChimeraFsException {
_fs.pin(new FsInode(_fs, ino()), lifetime);
}
}

/*
* This method allows overwrite only with ROOT access.
* It also allows a non-ROOT user to set only one checksum
* (type, value) pair.
*/
private void handleSetChecksum() throws ChimeraFsException {
if (_args.length != 3) {
throw new InvalidArgumentChimeraException("incorrect number of arguments.");
}

Set<Checksum> checksums = _fs.getInodeChecksums(this);
ChecksumType type = ChecksumType.getChecksumType(_args[1]);

try {
/*
* The filesystem implementation does not allow
* overwrite using the 'setInodeChecksum' method, so
* an explicit deletion is required.
*/
if (checksums.stream().anyMatch(c -> type.equals(c.getType()))) {
_fs.removeInodeChecksum(this, type.getType());
}

Checksum cks = new Checksum(type, _args[2]);
_fs.setInodeChecksum(this, type.getType(), cks.getValue());
} catch (IllegalArgumentException e) {
throw new InvalidArgumentChimeraException("Invalid checksum: "
+ _args[2]
+ "; "
+ e.getMessage());
}
}
}
Expand Up @@ -28,6 +28,7 @@
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.AccessController;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
Expand Down Expand Up @@ -96,6 +97,7 @@
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.dcache.chimera.FileSystemProvider.StatCacheOption.NO_STAT;
import static org.dcache.chimera.FileSystemProvider.StatCacheOption.STAT;
import static org.dcache.chimera.FsInodeType.PSET;
import static org.dcache.nfs.v4.xdr.nfs4_prot.ACCESS4_EXTEND;
import static org.dcache.nfs.v4.xdr.nfs4_prot.ACCESS4_MODIFY;
import static org.dcache.nfs.v4.xdr.nfs4_prot.ACE4_INHERIT_ONLY_ACE;
Expand Down Expand Up @@ -318,6 +320,9 @@ public Stat getattr(Inode inode) throws IOException {
public void setattr(Inode inode, Stat stat) throws IOException {
FsInode fsInode = toFsInode(inode);
try {
if (shouldRejectAttributeUpdates(fsInode)) {
throw new PermException("setStat not allowed.");
}
fsInode.setStat(toChimeraStat(stat));
} catch (InvalidArgumentChimeraException e) {
throw new InvalException(e.getMessage());
Expand Down Expand Up @@ -455,6 +460,18 @@ private boolean shouldRejectUpdates(FsInode fsInode) throws ChimeraFsException {
|| !_fs.getInodeLocations(fsInode, StorageGenericLocation.DISK).isEmpty());
}

private boolean shouldRejectAttributeUpdates(FsInode fsInode)
throws ChimeraFsException {
return fsInode.type() == PSET
&& ((FsInode_PSET)fsInode).isChecksum()
&& !isRoot()
&& !_fs.getInodeChecksums(fsInode).isEmpty();
}

private boolean isRoot() {
return Subjects.isRoot(Subject.getSubject(AccessController.getContext()));
}

@Override
public boolean hasIOLayout(Inode inode) throws IOException {
FsInode fsInode = toFsInode(inode);
Expand Down

0 comments on commit 0fb511c

Please sign in to comment.