All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hierynomus.smbj.share.Share Maven / Gradle / Ivy

There is a newer version: 0.13.0
Show newest version
/*
 * Copyright (C)2016 - SMBJ Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License 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.hierynomus.smbj.share;

import com.hierynomus.msdtyp.AccessMask;
import com.hierynomus.msdtyp.SecurityInformation;
import com.hierynomus.mserref.NtStatus;
import com.hierynomus.msfscc.FileAttributes;
import com.hierynomus.msfscc.FileInformationClass;
import com.hierynomus.msfscc.FileSystemInformationClass;
import com.hierynomus.mssmb2.*;
import com.hierynomus.mssmb2.messages.*;
import com.hierynomus.protocol.commons.concurrent.Futures;
import com.hierynomus.protocol.transport.TransportException;
import com.hierynomus.smbj.SmbConfig;
import com.hierynomus.smbj.common.SMBRuntimeException;
import com.hierynomus.smbj.common.SmbPath;
import com.hierynomus.smbj.connection.Connection;
import com.hierynomus.smbj.connection.NegotiatedProtocol;
import com.hierynomus.smbj.io.ArrayByteChunkProvider;
import com.hierynomus.smbj.io.ByteChunkProvider;
import com.hierynomus.smbj.io.EmptyByteChunkProvider;
import com.hierynomus.smbj.session.Session;

import java.io.IOException;
import java.util.EnumSet;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

public class Share implements AutoCloseable {
    private static final SMB2FileId ROOT_ID = new SMB2FileId(
        new byte[]{(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF},
        new byte[]{(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF}
    );

    private static final EnumSet SUCCESS = EnumSet.of(NtStatus.STATUS_SUCCESS);
    private static final EnumSet SUCCESS_OR_SYMLINK = EnumSet.of(NtStatus.STATUS_SUCCESS, NtStatus.STATUS_STOPPED_ON_SYMLINK);
    private static final EnumSet SUCCESS_OR_NO_MORE_FILES_OR_NO_SUCH_FILE = EnumSet.of(NtStatus.STATUS_SUCCESS, NtStatus.STATUS_NO_MORE_FILES, NtStatus.STATUS_NO_SUCH_FILE);
    private static final EnumSet SUCCESS_OR_EOF = EnumSet.of(NtStatus.STATUS_SUCCESS, NtStatus.STATUS_END_OF_FILE);

    protected final SmbPath smbPath;
    protected final TreeConnect treeConnect;
    private final long treeId;
    protected Session session;
    private final SMB2Dialect dialect;
    private final int readBufferSize;
    private final long readTimeout;
    private final int writeBufferSize;
    private final long writeTimeout;
    private final int transactBufferSize;
    private final long transactTimeout;
    private final long sessionId;
    private final AtomicBoolean disconnected = new AtomicBoolean(false);

    Share(SmbPath smbPath, TreeConnect treeConnect) {
        this.smbPath = smbPath;
        this.treeConnect = treeConnect;
        session = treeConnect.getSession();
        Connection connection = treeConnect.getConnection();
        NegotiatedProtocol negotiatedProtocol = connection.getNegotiatedProtocol();
        dialect = negotiatedProtocol.getDialect();
        SmbConfig config = connection.getConfig();
        readBufferSize = Math.min(config.getReadBufferSize(), negotiatedProtocol.getMaxReadSize());
        readTimeout = config.getReadTimeout();
        writeBufferSize = Math.min(config.getWriteBufferSize(), negotiatedProtocol.getMaxWriteSize());
        writeTimeout = config.getWriteTimeout();
        transactBufferSize = Math.min(config.getTransactBufferSize(), negotiatedProtocol.getMaxTransactSize());
        transactTimeout = config.getTransactTimeout();
        sessionId = session.getSessionId();
        treeId = treeConnect.getTreeId();
    }

    @Override
    public void close() throws IOException {
        if (!disconnected.getAndSet(true)) {
            treeConnect.close();
        }
    }

    public boolean isConnected() {
        return !disconnected.get();
    }

    public SmbPath getSmbPath() {
        return smbPath;
    }

    public TreeConnect getTreeConnect() {
        return treeConnect;
    }

    int getReadBufferSize() {
        return readBufferSize;
    }

    long getReadTimeout() {
        return readTimeout;
    }

    int getWriteBufferSize() {
        return writeBufferSize;
    }

    SMB2FileId openFileId(String path, SMB2ImpersonationLevel impersonationLevel, Set accessMask, Set fileAttributes, Set shareAccess, SMB2CreateDisposition createDisposition, Set createOptions) {
        return createFile(path, impersonationLevel, accessMask, fileAttributes, shareAccess, createDisposition, createOptions).getFileId();
    }

    SMB2CreateResponse createFile(String path, SMB2ImpersonationLevel impersonationLevel, Set accessMask, Set fileAttributes, Set shareAccess, SMB2CreateDisposition createDisposition, Set createOptions) {
        SMB2CreateRequest cr = new SMB2CreateRequest(
            dialect,
            sessionId, treeId,
            impersonationLevel,
            accessMask,
            fileAttributes,
            shareAccess,
            createDisposition,
            createOptions,
            path
        );
        SMB2CreateResponse resp = sendReceive(cr, "Create", path, getCreateSuccessStatus(), transactTimeout);
        return resp;
    }

    protected Set getCreateSuccessStatus() {
        return SUCCESS_OR_SYMLINK;
    }

    void flush(SMB2FileId fileId) throws SMBApiException {
        SMB2Flush flushReq = new SMB2Flush(
            dialect,
            fileId,
            sessionId, treeId
        );
        sendReceive(flushReq, "Flush", fileId, SUCCESS, writeTimeout);
    }

    void closeFileId(SMB2FileId fileId) throws SMBApiException {
        SMB2Close closeReq = new SMB2Close(dialect, sessionId, treeId, fileId);
        sendReceive(closeReq, "Close", fileId, EnumSet.of(NtStatus.STATUS_SUCCESS, NtStatus.STATUS_FILE_CLOSED), transactTimeout);
    }

    SMB2QueryInfoResponse queryInfo(SMB2FileId fileId, SMB2QueryInfoRequest.SMB2QueryInfoType infoType, Set securityInfo, FileInformationClass fileInformationClass, FileSystemInformationClass fileSystemInformationClass) {
        SMB2QueryInfoRequest qreq = new SMB2QueryInfoRequest(
            dialect,
            sessionId, treeId,
            fileId, infoType,
            fileInformationClass, fileSystemInformationClass, null, securityInfo
        );
        return sendReceive(qreq, "QueryInfo", fileId, SUCCESS, transactTimeout);
    }

    void setInfo(SMB2FileId fileId, SMB2SetInfoRequest.SMB2InfoType infoType, Set securityInfo, FileInformationClass fileInformationClass, byte[] buffer) {
        SMB2SetInfoRequest qreq = new SMB2SetInfoRequest(
            dialect,
            sessionId, treeId,
            infoType, fileId,
            fileInformationClass, securityInfo, buffer
        );
        sendReceive(qreq, "SetInfo", fileId, SUCCESS, transactTimeout);
    }

    SMB2QueryDirectoryResponse queryDirectory(SMB2FileId fileId, Set flags, FileInformationClass informationClass, String searchPattern) {
        SMB2QueryDirectoryRequest qdr = new SMB2QueryDirectoryRequest(
            dialect,
            sessionId, treeId,
            fileId, informationClass,
            flags,
            0,
            searchPattern,
            transactBufferSize
        );

        return sendReceive(qdr, "Query directory", fileId, SUCCESS_OR_NO_MORE_FILES_OR_NO_SUCH_FILE, transactTimeout);
    }

    SMB2WriteResponse write(SMB2FileId fileId, ByteChunkProvider provider) {
        SMB2WriteRequest wreq = new SMB2WriteRequest(
            dialect,
            fileId,
            sessionId, treeId,
            provider,
            writeBufferSize
        );
        return sendReceive(wreq, "Write", fileId, SUCCESS, writeTimeout);
    }

    SMB2ReadResponse read(SMB2FileId fileId, long offset, int length) {
        return receive(
            readAsync(fileId, offset, length),
            "Read",
            fileId,
            SUCCESS_OR_EOF,
            readTimeout
        );
    }

    Future readAsync(SMB2FileId fileId, long offset, int length) {
        SMB2ReadRequest rreq = new SMB2ReadRequest(
            dialect,
            fileId,
            sessionId, treeId,
            offset,
            Math.min(length, readBufferSize)
        );
        return send(rreq);
    }

    private static final EmptyByteChunkProvider EMPTY = new EmptyByteChunkProvider(0);

    /**
     * Sends a control code directly to a specified device driver, causing the corresponding device to perform the
     * corresponding operation.
     *
     * @param ctlCode the control code
     * @param isFsCtl true if the control code is an FSCTL; false if it is an IOCTL
     * @param inData  the control code dependent input data
     * @return the response data or null if the control code did not produce a response
     */
    public byte[] ioctl(long ctlCode, boolean isFsCtl, byte[] inData) {
        return ioctl(ROOT_ID, ctlCode, isFsCtl, inData, 0, inData.length);
    }

    /**
     * Sends a control code directly to a specified device driver, causing the corresponding device to perform the
     * corresponding operation.
     *
     * @param ctlCode  the control code
     * @param isFsCtl  true if the control code is an FSCTL; false if it is an IOCTL
     * @param inData   the control code dependent input data
     * @param inOffset the offset in inData where the input data starts
     * @param inLength the number of bytes from inData to send, starting at offset
     * @return the response data or null if the control code did not produce a response
     */
    public byte[] ioctl(long ctlCode, boolean isFsCtl, byte[] inData, int inOffset, int inLength) {
        return ioctl(ROOT_ID, ctlCode, isFsCtl, inData, inOffset, inLength);
    }

    /**
     * Sends a control code directly to a specified device driver, causing the corresponding device to perform the
     * corresponding operation.
     *
     * @param ctlCode   the control code
     * @param isFsCtl   true if the control code is an FSCTL; false if it is an IOCTL
     * @param inData    the control code dependent input data
     * @param inOffset  the offset in inData where the input data starts
     * @param inLength  the number of bytes from inData to send, starting at inOffset
     * @param outData   the buffer where the response data should be written
     * @param outOffset the offset in outData where the output data should be written
     * @param outLength the maximum amount of data to write in outData, starting at outOffset
     * @return the number of bytes written to outData
     */
    public int ioctl(long ctlCode, boolean isFsCtl, byte[] inData, int inOffset, int inLength, byte[] outData, int outOffset, int outLength) {
        return ioctl(ROOT_ID, ctlCode, isFsCtl, inData, inOffset, inLength, outData, outOffset, outLength);
    }

    byte[] ioctl(SMB2FileId fileId, long ctlCode, boolean isFsCtl, byte[] inData, int inOffset, int inLength) {
        return ioctl(fileId, ctlCode, isFsCtl, inData, inOffset, inLength, -1);
    }

    byte[] ioctl(SMB2FileId fileId, long ctlCode, boolean isFsCtl, byte[] inData, int inOffset, int inLength, int maxOutputResponse) {
        SMB2IoctlResponse response = ioctl(fileId, ctlCode, isFsCtl, new ArrayByteChunkProvider(inData, inOffset, inLength, 0), maxOutputResponse);
        return response.getOutputBuffer();
    }

    int ioctl(SMB2FileId fileId, long ctlCode, boolean isFsCtl, byte[] inData, int inOffset, int inLength, byte[] outData, int outOffset, int outLength) {
        SMB2IoctlResponse response = ioctl(fileId, ctlCode, isFsCtl, new ArrayByteChunkProvider(inData, inOffset, inLength, 0), outLength);
        int length = 0;
        if (outData != null) {
            byte[] outputBuffer = response.getOutputBuffer();
            length = Math.min(outLength, outputBuffer.length);
            System.arraycopy(outputBuffer, 0, outData, outOffset, length);
        }
        return length;
    }

    SMB2IoctlResponse ioctl(SMB2FileId fileId, long ctlCode, boolean isFsCtl, ByteChunkProvider inputData, int maxOutputResponse) {
        Future fut = ioctlAsync(fileId, ctlCode, isFsCtl, inputData, maxOutputResponse);
        return receive(fut, "IOCTL", fileId, SUCCESS, transactTimeout);
    }

    public Future ioctlAsync(long ctlCode, boolean isFsCtl, ByteChunkProvider inputData) {
        return ioctlAsync(ROOT_ID, ctlCode, isFsCtl, inputData, -1);
    }

    private Future ioctlAsync(SMB2FileId fileId, long ctlCode, boolean isFsCtl, ByteChunkProvider inputData, int maxOutputResponse) {
        ByteChunkProvider inData = inputData == null ? EMPTY : inputData;

        if (inData.bytesLeft() > transactBufferSize) {
            throw new SMBRuntimeException("Input data size exceeds maximum allowed by server: " + inData.bytesLeft() + " > " + transactBufferSize);
        }

        int maxResponse;
        if (maxOutputResponse < 0) {
            maxResponse = transactBufferSize;
        } else if (maxOutputResponse > transactBufferSize) {
            throw new SMBRuntimeException("Output data size exceeds maximum allowed by server: " + maxOutputResponse + " > " + transactBufferSize);
        } else {
            maxResponse = maxOutputResponse;
        }

        SMB2IoctlRequest ioreq = new SMB2IoctlRequest(dialect, sessionId, treeId, ctlCode, fileId, inData, isFsCtl, maxResponse);
        return send(ioreq);
    }

    private  T sendReceive(SMB2Packet request, String name, Object target, Set successResponses, long timeout) {
        Future fut = send(request);
        return receive(fut, name, target, successResponses, timeout);
    }

    private  Future send(SMB2Packet request) {
        if (!isConnected()) {
            throw new SMBRuntimeException(getClass().getSimpleName() + " has already been closed");
        }

        try {
            return session.send(request);
        } catch (TransportException e) {
            throw new SMBRuntimeException(e);
        }
    }

     T receive(Future fut, String name, Object target, Set successResponses, long timeout) {
        T resp = receive(fut, timeout);

        NtStatus status = resp.getHeader().getStatus();
        if (!successResponses.contains(status)) {
            throw new SMBApiException(resp.getHeader(), name + " failed for " + target);
        }
        return resp;
    }

     T receive(Future fut, long timeout) {
        T resp;
        try {
            if (timeout > 0) {
                resp = Futures.get(fut, timeout, TimeUnit.MILLISECONDS, TransportException.Wrapper);
            } else {
                resp = Futures.get(fut, TransportException.Wrapper);
            }
        } catch (TransportException e) {
            throw new SMBRuntimeException(e);
        }
        return resp;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy