NfsWriteRequest.java

/**
 * Copyright 2016-2018 Dell Inc. or its subsidiaries. All rights reserved.
 *
 * <p>Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file
 * except in compliance with the License. A copy of the License is located at
 *
 * <p>http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 * <p>or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package com.emc.ecs.nfsclient.nfs;

import com.emc.ecs.nfsclient.rpc.Credential;
import com.emc.ecs.nfsclient.rpc.Xdr;
import java.io.FileNotFoundException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

/**
 * The request, as specified by RFC 1813 (https://tools.ietf.org/html/rfc1813).
 *
 * <p>Procedure WRITE writes data to a file.
 *
 * @author seibed
 */
public class NfsWriteRequest extends NfsRequestBase {

  /**
   * The server is free to commit any part of the data and the metadata to stable storage, including
   * all or none, before returning a reply to the client.
   */
  public static final int UNSTABLE = 0;

  /**
   * The server must commit all of the data to stable storage and enough of the metadata to retrieve
   * the data before returning.
   */
  public static final int DATA_SYNC = 1;

  /**
   * The server must commit the data written plus all file system metadata to stable storage before
   * returning results.
   */
  public static final int FILE_SYNC = 2;

  /**
   * The position within the file at which the write is to begin. An offset of 0 means to write data
   * starting at the beginning of the file.
   */
  private final long _offset;

  /**
   * The number of bytes of data to be written (calculated from the <code>payload</code>). If <code>
   * size</code> is 0, the WRITE will succeed and return a <code>count</code> of 0, barring errors
   * due to permissions checking. The <code>size</code> of data must be less than or equal to the
   * value of the <code>wtmax</code> field in the FSINFO reply structure for the file system that
   * contains file. If greater, the server may write only <code>wtmax</code> bytes, resulting in a
   * short write.
   */
  private final int _size;

  /**
   * One of the values below.
   *
   * <ul>
   *   <li>UNSTABLE = 0 - Best effort, no promises.
   *   <li>DATA_SYNC = 1 - Commit all data to stable storage, plus enough metadata for retrieval,
   *       before returning.
   *   <li>FILE_SYNC = 2 - Commit all data and metadata to stable storage before returning.
   * </ul>
   */
  private final int _syncType;

  /** The data to be written to the file. */
  private final List<ByteBuffer> _payload;

  /**
   * Creates the request, as specified by RFC 1813 (https://tools.ietf.org/html/rfc1813).
   *
   * <p>Procedure WRITE writes data to a file.
   *
   * @param fileHandle The file handle for the file to which data is to be written. This must
   *     identify a file system object of type, NF3REG.
   * @param offset The position within the file at which the write is to begin. An offset of 0 means
   *     to write data starting at the beginning of the file.
   * @param payload The data to be written to the file.
   * @param syncType One of the values below.
   *     <ul>
   *       <li>UNSTABLE = 0 - Best effort, no promises.
   *       <li>DATA_SYNC = 1 - Commit all data to stable storage, plus enough metadata for
   *           retrieval, before returning.
   *       <li>FILE_SYNC = 2 - Commit all data and metadata to stable storage before returning.
   *     </ul>
   *
   * @param credential The credential used for RPC authentication.
   * @param nfsVersion The NFS version number
   */
  public NfsWriteRequest(
      byte[] fileHandle,
      long offset,
      List<ByteBuffer> payload,
      int syncType,
      Credential credential,
      int nfsVersion)
      throws FileNotFoundException {
    super(Nfs.RPC_PROGRAM, nfsVersion, Nfs.NFSPROC3_WRITE, credential, fileHandle);
    if ((syncType < 0) || (syncType > 2)) {
      throw new IllegalArgumentException("Invalid syncType: " + syncType);
    }

    _offset = offset;
    _syncType = syncType;
    _payload = payload == null ? null : new ArrayList<>(payload);
    int payload_size = 0;
    if (_payload != null) {
      for (ByteBuffer b : _payload) {
        payload_size += b.remaining();
      }
    }
    _size = payload_size;
  }

  /** {@inheritDoc} */
  @Override
  public void marshalling(Xdr xdr) {
    super.marshalling(xdr);
    xdr.putLong(_offset);
    xdr.putUnsignedInt(_size);
    xdr.putInt(_syncType);
    // opaque payload, pass payload size down to avoid enumerating the list
    // again
    if (_payload != null) {
      xdr.putPayloads(_payload, _size);
    }
  }

  /** {@inheritDoc} */
  @Override
  public String toString() {
    return startToString("NfsWriteRequest")
        .append(" offset:")
        .append(_offset)
        .append(" size:")
        .append(_size)
        .append(" syncType:")
        .append(_syncType)
        .toString();
  }

  /**
   * Returns the number of bytes of data to be written (calculated from the <code>payload</code>).
   * If <code>size</code> is 0, the WRITE will succeed and return a <code>count</code> of 0, barring
   * errors due to permissions checking. The <code>size</code> of data must be less than or equal to
   * the value of the <code>wtmax</code> field in the FSINFO reply structure for the file system
   * that contains file. If greater, the server may write only <code>wtmax</code> bytes, resulting
   * in a short write.
   */
  public int getSize() {
    return _size;
  }

  /**
   * Returns
   *
   * <ul>
   *   <li><code>true</code> if data is synced
   *   <li><code>false</code> if it is not.
   * </ul>
   *
   * .
   */
  public boolean isSync() {
    return _syncType == FILE_SYNC;
  }
}