1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-23 17:34:34 +03:00

Add support for offline files support, remote storage, and Async I/O force operations to VFS

Offline files support and remote storage are for allowing communication with
backup and archiving tools that mark files moved to a tape library as offline.
We translate this info into corresponding CIFS offline file attribute and
mark an exported volume as remote storage.

Async I/O force is to allow selective redirection of I/O operations to asynchronous
processing in case it is viable at VFS module discretion. It is needed for
proper handling of offline files as performing regular I/O on offline file will
block smbd.


Signed-off-by: Alexander Bokovoy <ab@samba.org>
This commit is contained in:
Alexander Bokovoy 2008-01-16 12:17:03 +03:00
parent 0c8e23afbb
commit 875208724e
7 changed files with 168 additions and 68 deletions

View File

@ -70,6 +70,7 @@
* timestamp resolition. JRA. */
/* Changed to version21 to add chflags operation -- jpeach */
/* Changed to version22 to add lchown operation -- jra */
/* Additional change: add operations for offline files and remote storage volume abstraction -- ab*/
/* Leave at 22 - not yet released. But change set_nt_acl to return an NTSTATUS. jra. */
/* Leave at 22 - not yet released. Add file_id_create operation. --metze */
/* Leave at 22 - not yet released. Change all BOOL parameters (int) to bool. jra. */
@ -257,6 +258,12 @@ typedef enum _vfs_op_type {
SMB_VFS_OP_AIO_ERROR,
SMB_VFS_OP_AIO_FSYNC,
SMB_VFS_OP_AIO_SUSPEND,
SMB_VFS_OP_AIO_FORCE,
/* offline operations */
SMB_VFS_OP_IS_OFFLINE,
SMB_VFS_OP_SET_OFFLINE,
SMB_VFS_OP_IS_REMOTESTORAGE,
/* This should always be last enum value */
@ -405,6 +412,12 @@ struct vfs_ops {
int (*aio_error_fn)(struct vfs_handle_struct *handle, struct files_struct *fsp, SMB_STRUCT_AIOCB *aiocb);
int (*aio_fsync)(struct vfs_handle_struct *handle, struct files_struct *fsp, int op, SMB_STRUCT_AIOCB *aiocb);
int (*aio_suspend)(struct vfs_handle_struct *handle, struct files_struct *fsp, const SMB_STRUCT_AIOCB * const aiocb[], int n, const struct timespec *timeout);
bool (*aio_force)(struct vfs_handle_struct *handle, struct files_struct *fsp);
/* offline operations */
int (*is_offline)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, bool *offline);
int (*set_offline)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path);
bool (*is_remotestorage)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path);
} ops;
@ -526,6 +539,12 @@ struct vfs_ops {
struct vfs_handle_struct *aio_error;
struct vfs_handle_struct *aio_fsync;
struct vfs_handle_struct *aio_suspend;
struct vfs_handle_struct *aio_force;
/* offline operations */
struct vfs_handle_struct *is_offline;
struct vfs_handle_struct *set_offline;
struct vfs_handle_struct *is_remotestorage;
} handles;
};

View File

@ -138,6 +138,12 @@
#define SMB_VFS_AIO_ERROR(fsp,aiocb) ((fsp)->conn->vfs.ops.aio_error_fn((fsp)->conn->vfs.handles.aio_error,(fsp),(aiocb)))
#define SMB_VFS_AIO_FSYNC(fsp,op,aiocb) ((fsp)->conn->vfs.ops.aio_fsync((fsp)->conn->vfs.handles.aio_fsync,(fsp),(op),(aiocb)))
#define SMB_VFS_AIO_SUSPEND(fsp,aiocb,n,ts) ((fsp)->conn->vfs.ops.aio_suspend((fsp)->conn->vfs.handles.aio_suspend,(fsp),(aiocb),(n),(ts)))
#define SMB_VFS_AIO_FORCE(fsp) ((fsp)->conn->vfs.ops.aio_force((fsp)->conn->vfs.handles.aio_force,(fsp)))
/* Offline operations */
#define SMB_VFS_IS_OFFLINE(conn,path,sbuf,offline) ((conn)->vfs.ops.is_offline((conn)->vfs.handles.is_offline,(conn),(path),(sbuf),(offline)))
#define SMB_VFS_SET_OFFLINE(conn,path) ((conn)->vfs.ops.set_offline((conn)->vfs.handles.set_offline,(conn),(path)))
#define SMB_VFS_IS_REMOTESTORAGE(conn,path) ((conn)->vfs.ops.is_remotestorage((conn)->vfs.handles.is_remotestorage,(conn),(path)))
/*******************************************************************
Don't access conn->vfs_opaque.ops directly!!!
@ -257,6 +263,12 @@
#define SMB_VFS_OPAQUE_AIO_ERROR(fsp,aiocb) ((fsp)->conn->vfs_opaque.ops.aio_error_fn((fsp)->conn->vfs_opaque.handles.aio_error,(fsp),(aiocb)))
#define SMB_VFS_OPAQUE_AIO_FSYNC(fsp,op,aiocb) ((fsp)->conn->vfs_opaque.ops.aio_fsync((fsp)->conn->vfs_opaque.handles.aio_fsync,(fsp),(op),(aiocb)))
#define SMB_VFS_OPAQUE_AIO_SUSPEND(fsp,aiocb,n,ts) ((fsp)->conn->vfs_opaque.ops.aio_suspend((fsp)->conn->vfs_opaque.handles.aio_suspend,(fsp),(aiocb),(n),(ts)))
#define SMB_VFS_OPAQUE_AIO_FORCE(fsp) ((fsp)->conn->vfs_opaque.ops.aio_force((fsp)->conn->vfs_opaque.handles.aio_force,(fsp)))
/* Offline operations */
#define SMB_VFS_OPAQUE_IS_OFFLINE(conn,path,sbuf,offline) ((conn)->vfs_opaque.ops.is_offline((conn)->vfs_opaque.handles.is_offline,(conn),(path),(sbuf),(offline)))
#define SMB_VFS_OPAQUE_SET_OFFLINE(conn,path) ((conn)->vfs_opaque.ops.set_offline((conn)->vfs_opaque.handles.set_offline,(conn),(path)))
#define SMB_VFS_OPAQUE_IS_REMOTESTORAGE(conn,path) ((conn)->vfs_opaque.ops.is_remotestorage((conn)->vfs_opaque.handles.is_remotestorage,(conn),(path)))
/*******************************************************************
Don't access handle->vfs_next.ops.* directly!!!
@ -377,5 +389,11 @@
#define SMB_VFS_NEXT_AIO_ERROR(handle,fsp,aiocb) ((handle)->vfs_next.ops.aio_error_fn((handle)->vfs_next.handles.aio_error,(fsp),(aiocb)))
#define SMB_VFS_NEXT_AIO_FSYNC(handle,fsp,op,aiocb) ((handle)->vfs_next.ops.aio_fsync((handle)->vfs_next.handles.aio_fsync,(fsp),(op),(aiocb)))
#define SMB_VFS_NEXT_AIO_SUSPEND(handle,fsp,aiocb,n,ts) ((handle)->vfs_next.ops.aio_suspend((handle)->vfs_next.handles.aio_suspend,(fsp),(aiocb),(n),(ts)))
#define SMB_VFS_NEXT_AIO_FORCE(handle,fsp) ((handle)->vfs_next.ops.aio_force((handle)->vfs_next.handles.aio_force,(fsp)))
/* Offline operations */
#define SMB_VFS_NEXT_IS_OFFLINE(conn,path,sbuf,offline) ((conn)->vfs_next.ops.is_offline((conn)->vfs_next.handles.is_offline,(conn),(path),(sbuf),(offline)))
#define SMB_VFS_NEXT_SET_OFFLINE(conn,path) ((conn)->vfs_next.ops.set_offline((conn)->vfs_next.handles.set_offline,(conn),(path)))
#define SMB_VFS_NEXT_IS_REMOTESTORAGE(conn,path) ((conn)->vfs_next.ops.is_remotestorage((conn)->vfs_next.handles.is_remotestorage,(conn),(path)))
#endif /* _VFS_MACROS_H */

View File

@ -1225,6 +1225,38 @@ static int vfswrap_aio_suspend(struct vfs_handle_struct *handle, struct files_st
return sys_aio_suspend(aiocb, n, timeout);
}
static int vfswrap_aio_force(struct vfs_handle_struct *handle, struct files_struct *fsp)
{
return False;
}
static int vfswrap_is_offline(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, bool *offline)
{
if (ISDOT(path) || ISDOTDOT(path)) {
return 1;
}
if (!lp_dmapi_support(SNUM(conn)) || !dmapi_have_session()) {
return -1;
}
*offline = (dmapi_file_flags(path) & FILE_ATTRIBUTE_OFFLINE) != 0;
return 0;
}
static int vfswrap_set_offline(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path)
{
/* We don't know how to set offline bit by default, needs to be overriden in the vfs modules */
return -1;
}
static bool vfswrap_is_remotestorage(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path)
{
/* We don't know how to detect that volume is remote storage. VFS modules should redefine it. */
return False;
}
static vfs_op_tuple vfs_default_ops[] = {
/* Disk operations */
@ -1442,6 +1474,16 @@ static vfs_op_tuple vfs_default_ops[] = {
{SMB_VFS_OP(vfswrap_aio_suspend),SMB_VFS_OP_AIO_SUSPEND,
SMB_VFS_LAYER_OPAQUE},
{SMB_VFS_OP(vfswrap_aio_force), SMB_VFS_OP_AIO_FORCE,
SMB_VFS_LAYER_OPAQUE},
{SMB_VFS_OP(vfswrap_is_offline),SMB_VFS_OP_IS_OFFLINE,
SMB_VFS_LAYER_OPAQUE},
{SMB_VFS_OP(vfswrap_set_offline),SMB_VFS_OP_SET_OFFLINE,
SMB_VFS_LAYER_OPAQUE},
{SMB_VFS_OP(vfswrap_is_remotestorage),SMB_VFS_OP_IS_REMOTESTORAGE,
SMB_VFS_LAYER_OPAQUE},
/* Finish VFS operations definition */
{SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP,

View File

@ -46,7 +46,7 @@ bool dmapi_have_session(void) { return False; }
#define DMAPI_SESSION_NAME "samba"
#define DMAPI_TRACE 10
static dm_sessid_t dmapi_session = DM_NO_SESSION;
static dm_sessid_t samba_dmapi_session = DM_NO_SESSION;
/* Initialise the DMAPI interface. Make sure that we only end up initialising
* once per process to avoid resource leaks across different DMAPI
@ -75,7 +75,7 @@ static int init_dmapi_service(void)
bool dmapi_have_session(void)
{
return dmapi_session != DM_NO_SESSION;
return samba_dmapi_session != DM_NO_SESSION;
}
static dm_sessid_t *realloc_session_list(dm_sessid_t * sessions, int count)
@ -110,7 +110,7 @@ int dmapi_init_session(void)
*/
SMB_WARN(getuid() == 0, "dmapi_init_session must be called as root");
dmapi_session = DM_NO_SESSION;
samba_dmapi_session = DM_NO_SESSION;
if (init_dmapi_service() < 0) {
return -1;
}
@ -139,7 +139,7 @@ retry:
err = dm_query_session(sessions[i], sizeof(buf), buf, &buflen);
buf[sizeof(buf) - 1] = '\0';
if (err == 0 && strcmp(DMAPI_SESSION_NAME, buf) == 0) {
dmapi_session = sessions[i];
samba_dmapi_session = sessions[i];
DEBUGADD(DMAPI_TRACE,
("attached to existing DMAPI session "
"named '%s'\n", buf));
@ -150,16 +150,15 @@ retry:
TALLOC_FREE(sessions);
/* No session already defined. */
if (dmapi_session == DM_NO_SESSION) {
err = dm_create_session(DM_NO_SESSION,
CONST_DISCARD(char *,
DMAPI_SESSION_NAME),
&dmapi_session);
if (samba_dmapi_session == DM_NO_SESSION) {
err = dm_create_session(DM_NO_SESSION,
CONST_DISCARD(char *, DMAPI_SESSION_NAME),
&samba_dmapi_session);
if (err < 0) {
DEBUGADD(DMAPI_TRACE,
("failed to create new DMAPI session: %s\n",
strerror(errno)));
dmapi_session = DM_NO_SESSION;
samba_dmapi_session = DM_NO_SESSION;
return -1;
}
@ -185,22 +184,22 @@ static int reattach_dmapi_session(void)
char buf[DM_SESSION_INFO_LEN];
size_t buflen;
if (dmapi_session != DM_NO_SESSION ) {
if (samba_dmapi_session != DM_NO_SESSION ) {
become_root();
/* NOTE: On Linux, this call opens /dev/dmapi, costing us a
* file descriptor. Ideally, we would close this when we fork.
*/
if (init_dmapi_service() < 0) {
dmapi_session = DM_NO_SESSION;
samba_dmapi_session = DM_NO_SESSION;
unbecome_root();
return -1;
}
if (dm_query_session(dmapi_session, sizeof(buf),
if (dm_query_session(samba_dmapi_session, sizeof(buf),
buf, &buflen) < 0) {
/* Session is stale. Disable DMAPI. */
dmapi_session = DM_NO_SESSION;
samba_dmapi_session = DM_NO_SESSION;
unbecome_root();
return -1;
}
@ -214,33 +213,42 @@ static int reattach_dmapi_session(void)
return 0;
}
uint32 dmapi_file_flags(const char * const path)
/* If a DMAPI session has been initialised, then we need to make sure
* we are attached to it and have the correct privileges. This is
* necessary to be able to do DMAPI operations across a fork(2). If
* it fails, there is no likelihood of that failure being transient.
*
* Note that this use of the static attached flag relies on the fact
* that dmapi_file_flags() is never called prior to forking the
* per-client server process.
*/
const void * dmapi_get_current_session(void)
{
static int attached = 0;
if (dmapi_have_session() && !attached) {
attached++;
if (reattach_dmapi_session() < 0) {
return DM_NO_SESSION;
}
}
return &samba_dmapi_session;
}
uint32 dmapi_file_flags(const char * const path)
{
dm_sessid_t dmapi_session;
int err;
dm_eventset_t events = {0};
uint nevents;
void *dm_handle;
size_t dm_handle_len;
void *dm_handle = NULL;
size_t dm_handle_len = 0;
uint32 flags = 0;
/* If a DMAPI session has been initialised, then we need to make sure
* we are attached to it and have the correct privileges. This is
* necessary to be able to do DMAPI operations across a fork(2). If
* it fails, there is no liklihood of that failure being transient.
*
* Note that this use of the static attached flag relies on the fact
* that dmapi_file_flags() is never called prior to forking the
* per-client server process.
*/
if (dmapi_have_session() && !attached) {
attached++;
if (reattach_dmapi_session() < 0) {
return 0;
}
dmapi_session = *(dm_sessid_t*) dmapi_get_current_session();
if (dmapi_session == DM_NO_SESSION) {
return 0;
}
/* AIX has DMAPI but no POSIX capablities support. In this case,
@ -313,4 +321,5 @@ done:
return flags;
}
#endif /* USE_DMAPI */

View File

@ -30,23 +30,6 @@ static int set_sparse_flag(const SMB_STRUCT_STAT * const sbuf)
return 0;
}
/****************************************************************************
Work out whether this file is offline
****************************************************************************/
static uint32 set_offline_flag(connection_struct *conn, const char *const path)
{
if (ISDOT(path) || ISDOTDOT(path)) {
return 0;
}
if (!lp_dmapi_support(SNUM(conn)) || !dmapi_have_session()) {
return 0;
}
return dmapi_file_flags(path);
}
/****************************************************************************
Change a dos mode to a unix mode.
Base permission for files:
@ -366,6 +349,8 @@ uint32 dos_mode_msdfs(connection_struct *conn, const char *path,SMB_STRUCT_STAT
uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
{
uint32 result = 0;
bool offline;
int ret;
DEBUG(8,("dos_mode: %s\n", path));
@ -395,8 +380,10 @@ uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
result |= dos_mode_from_sbuf(conn, path, sbuf);
}
if (S_ISREG(sbuf->st_mode)) {
result |= set_offline_flag(conn, path);
ret = SMB_VFS_IS_OFFLINE(conn, path, sbuf, &offline);
if (S_ISREG(sbuf->st_mode) && (ret == 0) && offline) {
result |= FILE_ATTRIBUTE_OFFLINE;
}
/* Optimization : Only call is_hidden_path if it's not already
@ -432,7 +419,7 @@ int file_set_dosmode(connection_struct *conn, const char *fname,
int mask=0;
mode_t tmp;
mode_t unixmode;
int ret = -1;
int ret = -1, lret = -1;
/* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
dosmode &= SAMBA_ATTRIBUTES_MASK;
@ -505,10 +492,21 @@ int file_set_dosmode(connection_struct *conn, const char *fname,
unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
}
if ((ret = SMB_VFS_CHMOD(conn,fname,unixmode)) == 0) {
if (!newfile) {
if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
lret = SMB_VFS_SET_OFFLINE(conn, fname);
if (lret == -1) {
DEBUG(0, ("set_dos_mode: client has asked to set "
"FILE_ATTRIBUTE_OFFLINE to %s/%s but there was "
"an error while setting it or it is not supported.\n",
parent_dir, fname));
}
}
ret = SMB_VFS_CHMOD(conn, fname, unixmode);
if (ret == 0) {
if(!newfile || (lret != -1)) {
notify_fname(conn, NOTIFY_ACTION_MODIFIED,
FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
}
st->st_mode = unixmode;
return 0;

View File

@ -3329,8 +3329,12 @@ void reply_read_and_X(struct smb_request *req)
return;
}
if (!big_readX
&& schedule_aio_read_and_X(conn, req, fsp, startpos, smb_maxcnt)) {
/* It is possible for VFS modules to selectively decide whether Async I/O should be used
for the file or not.
*/
if ((SMB_VFS_AIO_FORCE(fsp)) &&
!big_readX &&
schedule_aio_read_and_X(conn, req, fsp, startpos, smb_maxcnt)) {
END_PROFILE(SMBreadX);
return;
}
@ -4001,13 +4005,16 @@ void reply_write_and_X(struct smb_request *req)
nwritten = 0;
} else {
if (req->unread_bytes == 0 &&
schedule_aio_write_and_X(conn, req, fsp, data,
startpos, numtowrite)) {
/* It is possible for VFS modules to selectively decide whether Async I/O
should be used for the file or not.
*/
if ((SMB_VFS_AIO_FORCE(fsp)) && (req->unread_bytes == 0) &&
schedule_aio_write_and_X(conn, req, fsp, data, startpos,
numtowrite)) {
END_PROFILE(SMBwriteX);
return;
}
nwritten = write_file(req,fsp,data,startpos,numtowrite);
}

View File

@ -2373,8 +2373,8 @@ static void call_trans2qfsinfo(connection_struct *conn,
const char *vname = volume_label(SNUM(conn));
int snum = SNUM(conn);
char *fstype = lp_fstype(SNUM(conn));
int quota_flag = 0;
uint32 additional_flags = 0;
if (total_params < 2) {
reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
return;
@ -2487,16 +2487,23 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)st.st_dev, (unsi
case SMB_QUERY_FS_ATTRIBUTE_INFO:
case SMB_FS_ATTRIBUTE_INFORMATION:
additional_flags = 0;
#if defined(HAVE_SYS_QUOTAS)
quota_flag = FILE_VOLUME_QUOTAS;
additional_flags |= FILE_VOLUME_QUOTAS;
#endif
if(lp_nt_acl_support(SNUM(conn))) {
additional_flags |= FILE_PERSISTENT_ACLS;
}
if(SMB_VFS_IS_REMOTESTORAGE(conn, lp_pathname(SNUM(conn)))) {
additional_flags |= FILE_SUPPORTS_REMOTE_STORAGE;
additional_flags |= FILE_SUPPORTS_REPARSE_POINTS;
}
SIVAL(pdata,0,FILE_CASE_PRESERVED_NAMES|FILE_CASE_SENSITIVE_SEARCH|
(lp_nt_acl_support(SNUM(conn)) ? FILE_PERSISTENT_ACLS : 0)|
FILE_SUPPORTS_OBJECT_IDS|
FILE_UNICODE_ON_DISK|
quota_flag); /* FS ATTRIBUTES */
FILE_SUPPORTS_OBJECT_IDS|FILE_UNICODE_ON_DISK|
additional_flags); /* FS ATTRIBUTES */
SIVAL(pdata,4,255); /* Max filename component length */
/* NOTE! the fstype must *not* be null terminated or win98 won't recognise it