/*******************************************************************************
* Copyright (c) 2007, 2013 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.tcf.services;
import java.io.IOException;
import java.util.Map;
import org.eclipse.tcf.protocol.IService;
import org.eclipse.tcf.protocol.IToken;
/**
* File System service provides file transfer (and more generally file
* system access) functionality in TCF. The service design is
* derived from SSH File Transfer Protocol specifications.
*
* Request Synchronization and Reordering
*
* The protocol and implementations MUST process requests relating to
* the same file in the order in which they are received. In other
* words, if an application submits multiple requests to the server, the
* results in the responses will be the same as if it had sent the
* requests one at a time and waited for the response in each case. For
* example, the server may process non-overlapping read/write requests
* to the same file in parallel, but overlapping reads and writes cannot
* be reordered or parallelized. However, there are no ordering
* restrictions on the server for processing requests from two different
* file transfer connections. The server may interleave and parallelize
* them at will.
*
* There are no restrictions on the order in which responses to
* outstanding requests are delivered to the client, except that the
* server must ensure fairness in the sense that processing of no
* request will be indefinitely delayed even if the client is sending
* other requests so that there are multiple outstanding requests all
* the time.
*
* There is no limit on the number of outstanding (non-acknowledged)
* requests that the client may send to the server. In practice this is
* limited by the buffering available on the data stream and the queuing
* performed by the server. If the server's queues are full, it should
* not read any more data from the stream, and flow control will prevent
* the client from sending more requests.
*
* File Names
*
* This protocol represents file names as strings. File names are
* assumed to use the slash ('/') character as a directory separator.
*
* File names starting with a slash are "absolute", and are relative to
* the root of the file system. Names starting with any other character
* are relative to the user's default directory (home directory). Client
* can use 'user()' command to retrieve current user home directory.
*
* Servers SHOULD interpret a path name component ".." as referring to
* the parent directory, and "." as referring to the current directory.
* If the server implementation limits access to certain parts of the
* file system, it must be extra careful in parsing file names when
* enforcing such restrictions. There have been numerous reported
* security bugs where a ".." in a path name has allowed access outside
* the intended area.
*
* An empty path name is valid, and it refers to the user's default
* directory (usually the user's home directory).
*
* Otherwise, no syntax is defined for file names by this specification.
* Clients should not make any other assumptions; however, they can
* splice path name components returned by readdir() together
* using a slash ('/') as the separator, and that will work as expected.
*
* @noimplement This interface is not intended to be implemented by clients.
*/
public interface IFileSystem extends IService {
/**
* Service name.
*/
static final String NAME = "FileSystem";
/**
* Flags to be used with open() method.
*/
static final int
/**
* Open the file for reading.
*/
TCF_O_READ = 0x00000001,
/**
* Open the file for writing. If both this and TCF_O_READ are
* specified, the file is opened for both reading and writing.
*/
TCF_O_WRITE = 0x00000002,
/**
* Force all writes to append data at the end of the file.
*/
TCF_O_APPEND = 0x00000004,
/**
* If this flag is specified, then a new file will be created if one
* does not already exist (if TCF_O_TRUNC is specified, the new file will
* be truncated to zero length if it previously exists).
*/
TCF_O_CREAT = 0x00000008,
/**
* Forces an existing file with the same name to be truncated to zero
* length when creating a file by specifying TCF_O_CREAT.
* TCF_O_CREAT MUST also be specified if this flag is used.
*/
TCF_O_TRUNC = 0x00000010,
/**
* Causes the request to fail if the named file already exists.
* TCF_O_CREAT MUST also be specified if this flag is used.
*/
TCF_O_EXCL = 0x00000020;
/**
* Flags to be used together with FileAttrs.
* The flags specify which of the fields are present. Those fields
* for which the corresponding flag is not set are not present (not
* included in the message).
*/
static final int
ATTR_SIZE = 0x00000001,
ATTR_UIDGID = 0x00000002,
ATTR_PERMISSIONS = 0x00000004,
ATTR_ACMODTIME = 0x00000008;
/**
* FileAttrs is used both when returning file attributes from
* the server and when sending file attributes to the server. When
* sending it to the server, the flags field specifies which attributes
* are included, and the server will use default values for the
* remaining attributes (or will not modify the values of remaining
* attributes). When receiving attributes from the server, the flags
* specify which attributes are included in the returned data. The
* server normally returns all attributes it knows about.
*/
final static class FileAttrs {
/**
* The `flags' specify which of the fields are present.
*/
public final int flags;
/**
* The `size' field specifies the size of the file in bytes.
*/
public final long size;
/**
* The `uid' and `gid' fields contain numeric Unix-like user and group
* identifiers, respectively.
*/
public final int uid;
public final int gid;
/**
* The `permissions' field contains a bit mask of file permissions as
* defined by posix [1].
*/
public final int permissions;
/**
* The `atime' and `mtime' contain the access and modification times of
* the files, respectively. They are represented as milliseconds from
* midnight Jan 1, 1970 in UTC.
*/
public final long atime;
public final long mtime;
/**
* Additional (non-standard) attributes.
*/
public final Map<String,Object> attributes;
public FileAttrs(int flags, long size, int uid, int gid,
int permissions, long atime, long mtime, Map<String,Object> attributes) {
this.flags = flags;
this.size = size;
this.uid = uid;
this.gid = gid;
this.permissions = permissions;
this.atime = atime;
this.mtime = mtime;
this.attributes = attributes;
}
/**
* Determines if the file system object is a file on the remote file system.
*
* @return true if and only if the object on the remote system can be considered to have "contents" that
* have the potential to be read and written as a byte stream.
*/
public boolean isFile() {
if ((flags & ATTR_PERMISSIONS) == 0) return false;
return (permissions & S_IFMT) == S_IFREG;
}
/**
* Determines if the file system object is a directory on the remote file system.
*
* @return true if and only if the object on the remote system is a directory.
* That is, it contains entries that can be interpreted as other files.
*/
public boolean isDirectory() {
if ((flags & ATTR_PERMISSIONS) == 0) return false;
return (permissions & S_IFMT) == S_IFDIR;
}
}
/**
* The following flags are defined for the 'permissions' field:
*/
static final int
S_IFMT = 0170000, // bitmask for the file type bitfields
S_IFSOCK = 0140000, // socket
S_IFLNK = 0120000, // symbolic link
S_IFREG = 0100000, // regular file
S_IFBLK = 0060000, // block device
S_IFDIR = 0040000, // directory
S_IFCHR = 0020000, // character device
S_IFIFO = 0010000, // fifo
S_ISUID = 0004000, // set UID bit
S_ISGID = 0002000, // set GID bit (see below)
S_ISVTX = 0001000, // sticky bit (see below)
S_IRWXU = 00700, // mask for file owner permissions
S_IRUSR = 00400, // owner has read permission
S_IWUSR = 00200, // owner has write permission
S_IXUSR = 00100, // owner has execute permission
S_IRWXG = 00070, // mask for group permissions
S_IRGRP = 00040, // group has read permission
S_IWGRP = 00020, // group has write permission
S_IXGRP = 00010, // group has execute permission
S_IRWXO = 00007, // mask for permissions for others (not in group)
S_IROTH = 00004, // others have read permission
S_IWOTH = 00002, // others have write permission
S_IXOTH = 00001; // others have execute permission
final static class DirEntry {
/**
* `filename' is a file name being returned. It is a relative name within
* the directory, without any path components;
*/
public final String filename;
/**
* `longname' is an expanded format for the file name, similar to what
* is returned by "ls -l" on Unix systems.
* The format of the `longname' field is unspecified by this protocol.
* It MUST be suitable for use in the output of a directory listing
* command (in fact, the recommended operation for a directory listing
* command is to simply display this data). However, clients SHOULD NOT
* attempt to parse the longname field for file attributes; they SHOULD
* use the attrs field instead.
*/
public final String longname;
/**
* `attrs' is the attributes of the file.
*/
public final FileAttrs attrs;
public DirEntry(String filename, String longname, FileAttrs attrs) {
this.filename = filename;
this.longname = longname;
this.attrs = attrs;
}
}
/**
* Opaque representation of open file handle.
* Note: open file handle can be used only with service instance that
* created the handle.
*/
interface IFileHandle {
IFileSystem getService();
}
/**
* Service specific error codes.
*/
static final int
/**
* Indicates end-of-file condition; for read() it means that no
* more data is available in the file, and for readdir() it
* indicates that no more files are contained in the directory.
*/
STATUS_EOF = 0x10001,
/**
* This code is returned when a reference is made to a file which
* should exist but doesn't.
*/
STATUS_NO_SUCH_FILE = 0x10002,
/**
* is returned when the authenticated user does not have sufficient
* permissions to perform the operation.
*/
STATUS_PERMISSION_DENIED = 0x10003;
/**
* The class to represent File System error reports.
*/
@SuppressWarnings("serial")
abstract static class FileSystemException extends IOException {
protected FileSystemException(String message) {
super(message);
}
protected FileSystemException(Exception x) {
super(x.getMessage());
initCause(x);
}
/**
* Get error code. The code can be standard TCF error code or
* one of service specific codes, see STATUS_*.
* @return error code.
*/
public abstract int getStatus();
}
/**
* Open or create a file on a remote system.
*
* @param file_name specifies the file name. See 'File Names' for more information.
* @param flags is a bit mask of TCF_O_* flags.
* @param attrs specifies the initial attributes for the file.
* Default values will be used for those attributes that are not specified.
* @param done is call back object.
* @return pending command handle.
*/
IToken open(String file_name, int flags, FileAttrs attrs, DoneOpen done);
interface DoneOpen {
void doneOpen(IToken token, FileSystemException error, IFileHandle handle);
}
/**
* Close a file on a remote system.
*
* @param handle is a handle previously returned in the response to
* open() or opendir().
* @param done is call back object.
* @return pending command handle.
*/
IToken close(IFileHandle handle, DoneClose done);
interface DoneClose {
void doneClose(IToken token, FileSystemException error);
}
/**
* Read bytes from an open file.
* In response to this request, the server will read as many bytes as it
* can from the file (up to `len'), and return them in a byte array.
* If an error occurs or EOF is encountered, the server may return
* fewer bytes then requested. Call back method doneRead() argument 'error'
* will be not null in case of error, and argument 'eof' will be
* true in case of EOF. For normal disk files, it is guaranteed
* that this will read the specified number of bytes, or up to end of file
* or error. For e.g. device files this may return fewer bytes than requested.
*
* @param handle is an open file handle returned by open().
* @param offset is the offset (in bytes) relative
* to the beginning of the file from where to start reading.
* If offset < 0 then reading starts from current position in the file.
* @param len is the maximum number of bytes to read.
* @param done is call back object.
* @return pending command handle.
*/
IToken read(IFileHandle handle, long offset, int len, DoneRead done);
interface DoneRead {
void doneRead(IToken token, FileSystemException error, byte[] data, boolean eof);
}
/**
* Write bytes into an open file.
* The write will extend the file if writing beyond the end of the file.
* It is legal to write way beyond the end of the file; the semantics
* are to write zeroes from the end of the file to the specified offset
* and then the data.
*
* @param handle is an open file handle returned by open().
* @param offset is the offset (in bytes) relative
* to the beginning of the file from where to start writing.
* If offset < 0 then writing starts from current position in the file.
* @param data is byte array that contains data for writing.
* @param data_pos if offset in 'data' of first byte to write.
* @param data_size is the number of bytes to write.
* @param done is call back object.
* @return pending command handle.
*/
IToken write(IFileHandle handle, long offset,
byte[] data, int data_pos, int data_size, DoneWrite done);
interface DoneWrite {
void doneWrite(IToken token, FileSystemException error);
}
/**
* Retrieve file attributes.
*
* @param path - specifies the file system object for which
* status is to be returned.
* @param done is call back object.
* @return pending command handle.
*/
IToken stat(String path, DoneStat done);
/**
* Retrieve file attributes.
* Unlike 'stat()', 'lstat()' does not follow symbolic links.
*
* @param path - specifies the file system object for which
* status is to be returned.
* @param done is call back object.
* @return pending command handle.
*/
IToken lstat(String path, DoneStat done);
/**
* Retrieve file attributes for an open file (identified by the file handle).
*
* @param handle is a file handle returned by 'open()'.
* @param done is call back object.
* @return pending command handle.
*/
IToken fstat(IFileHandle handle, DoneStat done);
interface DoneStat {
void doneStat(IToken token, FileSystemException error, FileAttrs attrs);
}
/**
* Set file attributes.
* This request is used for operations such as changing the ownership,
* permissions or access times, as well as for truncating a file.
* An error will be returned if the specified file system object does
* not exist or the user does not have sufficient rights to modify the
* specified attributes.
*
* @param path specifies the file system object (e.g. file or directory)
* whose attributes are to be modified.
* @param attrs specifies the modifications to be made to file attributes.
* @param done is call back object.
* @return pending command handle.
*/
IToken setstat(String path, FileAttrs attrs, DoneSetStat done);
/**
* Set file attributes for an open file (identified by the file handle).
* This request is used for operations such as changing the ownership,
* permissions or access times, as well as for truncating a file.
*
* @param handle is a file handle returned by 'open()'.
* @param attrs specifies the modifications to be made to file attributes.
* @param done is call back object.
* @return pending command handle.
*/
IToken fsetstat(IFileHandle handle, FileAttrs attrs, DoneSetStat done);
interface DoneSetStat {
void doneSetStat(IToken token, FileSystemException error);
}
/**
* The opendir() command opens a directory for reading.
* Once the directory has been successfully opened, files (and
* directories) contained in it can be listed using readdir() requests.
* When the client no longer wishes to read more names from the
* directory, it SHOULD call close() for the handle. The handle
* should be closed regardless of whether an error has occurred or not.
* @param path - name of the directory to be listed (without any trailing slash).
* @param done - result call back object.
* @return pending command handle.
*/
IToken opendir(String path, DoneOpen done);
/**
* The files in a directory can be listed using the opendir() and
* readdir() requests. Each readdir() request returns one
* or more file names with full file attributes for each file. The
* client should call readdir() repeatedly until it has found the
* file it is looking for or until the server responds with a
* message indicating an error or end of file. The client should then
* close the handle using the close() request.
* Note: directory entries "." and ".." are NOT included into readdir()
* response.
* @param handle - file handle created by opendir()
* @param done - result call back object.
* @return pending command handle.
*/
IToken readdir(IFileHandle handle, DoneReadDir done);
interface DoneReadDir {
void doneReadDir(IToken token, FileSystemException error, DirEntry[] entries, boolean eof);
}
/**
* Create a directory on the server.
*
* @param path - specifies the directory to be created.
* @param attrs - new directory attributes.
* @param done - result call back object.
* @return pending command handle.
*/
IToken mkdir(String path, FileAttrs attrs, DoneMkDir done);
interface DoneMkDir {
void doneMkDir(IToken token, FileSystemException error);
}
/**
* Remove a directory.
* An error will be returned if no directory
* with the specified path exists, or if the specified directory is not
* empty, or if the path specified a file system object other than a
* directory.
*
* @param path - specifies the directory to be removed.
* @param done - result call back object.
* @return pending command handle.
*/
IToken rmdir(String path, DoneRemove done);
interface DoneRemove {
void doneRemove(IToken token, FileSystemException error);
}
/**
* Retrieve file system roots - top level file system objects.
* UNIX file system can report just one root with path "/". Other types of systems
* can have more the one root. For example, Windows server can return multiple roots:
* one per disc (e.g. "/C:/", "/D:/", etc.). Note: even Windows implementation of
* the service must use forward slash as directory separator, and must start
* absolute path with "/". Server should implement proper translation of
* protocol file names to OS native names and back.
*
* @param done - result call back object.
* @return pending command handle.
*/
IToken roots(DoneRoots done);
interface DoneRoots {
void doneRoots(IToken token, FileSystemException error, DirEntry[] entries);
}
/**
* Remove a file or symbolic link.
* This request cannot be used to remove directories.
*
* @param file_name is the name of the file to be removed.
* @param done - result call back object.
* @return pending command handle.
*/
IToken remove(String file_name, DoneRemove done);
/**
* Canonicalize any given path name to an absolute path.
* This is useful for converting path names containing ".." components or
* relative pathnames without a leading slash into absolute paths.
*
* @param path specifies the path name to be canonicalized.
* @param done - result call back object.
* @return pending command handle.
*/
IToken realpath(String path, DoneRealPath done);
interface DoneRealPath {
void doneRealPath(IToken token, FileSystemException error, String path);
}
/**
* Rename a file.
* It is an error if there already exists a file
* with the name specified by 'new_path'. The server may also fail rename
* requests in other situations, for example if `old_path' and `new_path'
* point to different file systems on the server.
*
* @param old_path is the name of an existing file or directory.
* @param new_path is the new name for the file or directory.
* @param done - result call back object.
* @return pending command handle.
*/
IToken rename(String old_path, String new_path, DoneRename done);
interface DoneRename {
void doneRename(IToken token, FileSystemException error);
}
/**
* Read the target of a symbolic link.
*
* @param path specifies the path name of the symbolic link to be read.
* @param done - result call back object.
* @return pending command handle.
*/
IToken readlink(String path, DoneReadLink done);
interface DoneReadLink {
void doneReadLink(IToken token, FileSystemException error, String path);
}
/**
* Create a symbolic link on the server.
*
* @param link_path specifies the path name of the symbolic link to be created.
* @param target_path specifies the target of the symbolic link.
* @param done - result call back object.
* @return pending command handle.
*/
IToken symlink(String link_path, String target_path, DoneSymLink done);
interface DoneSymLink {
void doneSymLink(IToken token, FileSystemException error);
}
/**
* Copy a file on remote system.
*
* @param src_path specifies the path name of the file to be copied.
* @param dst_path specifies destination file name.
* @param copy_permissions - if true then copy source file permissions.
* @param copy_ownership - if true then copy source file UID and GID.
* @param done - result call back object.
* @return pending command handle.
*/
IToken copy(String src_path, String dst_path,
boolean copy_permissions, boolean copy_ownership, DoneCopy done);
interface DoneCopy {
void doneCopy(IToken token, FileSystemException error);
}
/**
* Retrieve information about user account, which is used by server
* to access file system on behalf of the client.
*
* @param done - result call back object.
* @return pending command handle.
*/
IToken user(DoneUser done);
interface DoneUser {
void doneUser(IToken token, FileSystemException error,
int real_uid, int effective_uid, int real_gid, int effective_gid,
String home);
}
}