From 3592ccb9bc5343feb82304f999bee8a7589b7c3b Mon Sep 17 00:00:00 2001 From: SATOH Fumiyasu Date: Wed, 27 Feb 2013 02:51:08 +0900 Subject: [PATCH] libsmbclient: Add SMBC_open2_ctx to support share_access flags --- source3/include/libsmb_internal.h | 13 ++ source3/include/libsmbclient.h | 26 ++++ source3/libsmb/libsmb_compat.c | 42 +++++++ source3/libsmb/libsmb_context.c | 2 + source3/libsmb/libsmb_file.c | 237 +++++++++++++++++++++++++++++++++++++ source3/libsmb/libsmb_setget.c | 24 ++++ 6 files changed, 344 insertions(+) diff --git a/source3/include/libsmb_internal.h b/source3/include/libsmb_internal.h index 61dc95e..dac44a8 100644 --- a/source3/include/libsmb_internal.h +++ b/source3/include/libsmb_internal.h @@ -347,6 +347,19 @@ SMBC_creat_ctx(SMBCCTX *context, const char *path, mode_t mode); +SMBCFILE * +SMBC_open2_ctx(SMBCCTX *context, + const char *fname, + int flags, + mode_t mode, + unsigned long share_access); + +SMBCFILE * +SMBC_creat2_ctx(SMBCCTX *context, + const char *path, + mode_t mode, + unsigned long share_access); + ssize_t SMBC_read_ctx(SMBCCTX *context, SMBCFILE *file, diff --git a/source3/include/libsmbclient.h b/source3/include/libsmbclient.h index ccf80da..135ba9b 100644 --- a/source3/include/libsmbclient.h +++ b/source3/include/libsmbclient.h @@ -162,6 +162,11 @@ typedef enum smbc_share_mode SMBC_SHAREMODE_DENY_FCB = 7 } smbc_share_mode; +/* Values for the option "share_access" of smbc_open2_fn and smbc_creat2_fn */ +#define SMBC_SHAREACCESS_NONE 0 /* Cannot be used in bitmask. */ +#define SMBC_SHAREACCESS_READ 1 +#define SMBC_SHAREACCESS_WRITE 2 +#define SMBC_SHAREACCESS_DELETE 4 /** * Values for option SMB Encryption Level, as set and retrieved with @@ -836,6 +841,21 @@ typedef SMBCFILE * (*smbc_creat_fn)(SMBCCTX *c, smbc_creat_fn smbc_getFunctionCreat(SMBCCTX *c); void smbc_setFunctionCreat(SMBCCTX *c, smbc_creat_fn); +typedef SMBCFILE * (*smbc_open2_fn)(SMBCCTX *c, + const char *fname, + int flags, + mode_t mode, + unsigned long share_access); +smbc_open2_fn smbc_getFunctionOpen2(SMBCCTX *c); +void smbc_setFunctionOpen2(SMBCCTX *c, smbc_open2_fn fn); + +typedef SMBCFILE * (*smbc_creat2_fn)(SMBCCTX *c, + const char *path, + mode_t mode, + unsigned long share_access); +smbc_creat2_fn smbc_getFunctionCreat2(SMBCCTX *c); +void smbc_setFunctionCreat2(SMBCCTX *c, smbc_creat2_fn); + typedef ssize_t (*smbc_read_fn)(SMBCCTX *c, SMBCFILE *file, void *buf, @@ -1224,6 +1244,8 @@ SMBCCTX * smbc_set_context(SMBCCTX * new_context); int smbc_open(const char *furl, int flags, mode_t mode); +int smbc_open2(const char *furl, int flags, mode_t mode, unsigned long share_access); + /**@ingroup file * Create a file on an SMB server. * @@ -1258,6 +1280,8 @@ int smbc_open(const char *furl, int flags, mode_t mode); int smbc_creat(const char *furl, mode_t mode); +int smbc_creat2(const char *furl, mode_t mode, unsigned long share_access); + /**@ingroup file * Read from a file using an opened file handle. * @@ -2891,6 +2915,8 @@ struct _SMBCCTX */ smbc_open_fn open DEPRECATED_SMBC_INTERFACE; smbc_creat_fn creat DEPRECATED_SMBC_INTERFACE; + smbc_open2_fn open2 DEPRECATED_SMBC_INTERFACE; + smbc_creat2_fn creat2 DEPRECATED_SMBC_INTERFACE; smbc_read_fn read DEPRECATED_SMBC_INTERFACE; smbc_write_fn write DEPRECATED_SMBC_INTERFACE; smbc_unlink_fn unlink DEPRECATED_SMBC_INTERFACE; diff --git a/source3/libsmb/libsmb_compat.c b/source3/libsmb/libsmb_compat.c index 5b2ef2d..ae2d6ed 100644 --- a/source3/libsmb/libsmb_compat.c +++ b/source3/libsmb/libsmb_compat.c @@ -195,6 +195,48 @@ smbc_creat(const char *furl, } +int +smbc_open2(const char *furl, + int flags, + mode_t mode, + unsigned long share_access) +{ + SMBCFILE * file; + int fd; + + file = smbc_getFunctionOpen2(statcont)(statcont, furl, flags, mode, share_access); + if (!file) + return -1; + + fd = add_fd(file); + if (fd == -1) + smbc_getFunctionClose(statcont)(statcont, file); + return fd; +} + + +int +smbc_creat2(const char *furl, + mode_t mode, + unsigned long share_access) +{ + SMBCFILE * file; + int fd; + + file = smbc_getFunctionCreat2(statcont)(statcont, furl, mode, share_access); + if (!file) + return -1; + + fd = add_fd(file); + if (fd == -1) { + /* Hmm... should we delete the file too ? I guess we could try */ + smbc_getFunctionClose(statcont)(statcont, file); + smbc_getFunctionUnlink(statcont)(statcont, furl); + } + return fd; +} + + ssize_t smbc_read(int fd, void *buf, diff --git a/source3/libsmb/libsmb_context.c b/source3/libsmb/libsmb_context.c index c9ca455..76ce080 100644 --- a/source3/libsmb/libsmb_context.c +++ b/source3/libsmb/libsmb_context.c @@ -191,6 +191,8 @@ smbc_new_context(void) smbc_setFunctionOpen(context, SMBC_open_ctx); smbc_setFunctionCreat(context, SMBC_creat_ctx); + smbc_setFunctionOpen2(context, SMBC_open2_ctx); + smbc_setFunctionCreat2(context, SMBC_creat2_ctx); smbc_setFunctionRead(context, SMBC_read_ctx); smbc_setFunctionWrite(context, SMBC_write_ctx); smbc_setFunctionClose(context, SMBC_close_ctx); diff --git a/source3/libsmb/libsmb_file.c b/source3/libsmb/libsmb_file.c index fde7122..2237703 100644 --- a/source3/libsmb/libsmb_file.c +++ b/source3/libsmb/libsmb_file.c @@ -27,6 +27,45 @@ #include "libsmbclient.h" #include "libsmb_internal.h" +static int unix_o_flags2nt_flags(int flags, + uint32_t *desired_access, + uint32_t *create_disposition) +{ + int rw_flags = flags & (O_RDONLY | O_WRONLY | O_RDWR); + + if (rw_flags == O_RDONLY) { + *desired_access = SEC_FILE_READ_DATA; + } else if (rw_flags == O_WRONLY) { + *desired_access = SEC_FILE_WRITE_DATA; + } else if (rw_flags == O_RDWR) { + *desired_access = SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA; + } else { + return -1; + } + if (flags & O_APPEND) { + *desired_access |= SEC_FILE_APPEND_DATA; + } + + if (flags & O_TRUNC) { + if (flags & O_CREAT) { + *create_disposition = FILE_OVERWRITE_IF; + } else { + *create_disposition = FILE_OVERWRITE; + } + } else { + if (flags & O_CREAT) { + if (flags & O_EXCL) { + *create_disposition = FILE_CREATE; + } else { + *create_disposition = FILE_OPEN_IF; + } + } else { + *create_disposition = FILE_OPEN; + } + } + + return 0; +} /* * Routine to open() a file ... @@ -196,6 +235,189 @@ SMBC_open_ctx(SMBCCTX *context, return NULL; } +SMBCFILE * +SMBC_open2_ctx(SMBCCTX *context, + const char *fname, + int flags, + mode_t mode, + unsigned long share_access) +{ + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *workgroup = NULL; + char *path = NULL; + char *targetpath = NULL; + struct cli_state *targetcli = NULL; + SMBCSRV *srv = NULL; + SMBCFILE *file = NULL; + uint16_t fd; + NTSTATUS status = NT_STATUS_OBJECT_PATH_INVALID; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->internal->initialized) { + errno = EINVAL; /* Best I can think of ... */ + TALLOC_FREE(frame); + return NULL; + } + + if (!fname) { + errno = EINVAL; + TALLOC_FREE(frame); + return NULL; + } + + if (SMBC_parse_path(frame, + context, + fname, + &workgroup, + &server, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return NULL; + } + + if (!user || user[0] == (char)0) { + user = talloc_strdup(frame, smbc_getUser(context)); + if (!user) { + errno = ENOMEM; + TALLOC_FREE(frame); + return NULL; + } + } + + srv = SMBC_server(frame, context, True, + server, share, &workgroup, &user, &password); + if (!srv) { + if (errno == EPERM) errno = EACCES; + TALLOC_FREE(frame); + return NULL; /* SMBC_server sets errno */ + } + + /* Hmmm, the test for a directory is suspect here ... FIXME */ + + if (strlen(path) > 0 && path[strlen(path) - 1] == '\\') { + status = NT_STATUS_OBJECT_PATH_INVALID; + } else { + uint32_t desired_access; + uint32_t create_disposition; + + file = SMB_MALLOC_P(SMBCFILE); + if (!file) { + errno = ENOMEM; + TALLOC_FREE(frame); + return NULL; + } + + ZERO_STRUCTP(file); + + /*d_printf(">>>open: resolving %s\n", path);*/ + if (!cli_resolve_path(frame, "", context->internal->auth_info, + srv->cli, path, + &targetcli, &targetpath)) { + DEBUG(0, ("Could not resolve %s\n", path)); + errno = ENOENT; + SAFE_FREE(file); + TALLOC_FREE(frame); + return NULL; + } + /*d_printf(">>>open: resolved %s as %s\n", path, targetpath);*/ + + if (unix_o_flags2nt_flags(flags, &desired_access, + &create_disposition) == -1) { + SAFE_FREE(file); + TALLOC_FREE(frame); + errno = EINVAL; + return NULL; + } + + status = cli_ntcreate(targetcli, targetpath, + 0, /* create_flags */ + desired_access, + 0, /* file_attrs */ + share_access, + create_disposition, + 0, /* create_options */ + 0, /* security_flags */ + &fd); + if (!NT_STATUS_IS_OK(status)) { + + /* Handle the error ... */ + + SAFE_FREE(file); + errno = SMBC_errno(context, targetcli); + TALLOC_FREE(frame); + return NULL; + } + + /* Fill in file struct */ + + file->cli_fd = fd; + file->fname = SMB_STRDUP(fname); + file->srv = srv; + file->offset = 0; + file->file = True; + + DLIST_ADD(context->internal->files, file); + + /* + * If the file was opened in O_APPEND mode, all write + * operations should be appended to the file. To do that, + * though, using this protocol, would require a getattrE() + * call for each and every write, to determine where the end + * of the file is. (There does not appear to be an append flag + * in the protocol.) Rather than add all of that overhead of + * retrieving the current end-of-file offset prior to each + * write operation, we'll assume that most append operations + * will continuously write, so we'll just set the offset to + * the end of the file now and hope that's adequate. + * + * Note to self: If this proves inadequate, and O_APPEND + * should, in some cases, be forced for each write, add a + * field in the context options structure, for + * "strict_append_mode" which would select between the current + * behavior (if FALSE) or issuing a getattrE() prior to each + * write and forcing the write to the end of the file (if + * TRUE). Adding that capability will likely require adding + * an "append" flag into the _SMBCFILE structure to track + * whether a file was opened in O_APPEND mode. -- djl + */ + if (flags & O_APPEND) { + if (SMBC_lseek_ctx(context, file, 0, SEEK_END) < 0) { + (void) SMBC_close_ctx(context, file); + errno = ENXIO; + TALLOC_FREE(frame); + return NULL; + } + } + + TALLOC_FREE(frame); + return file; + } + + /* Check if opendir needed ... */ + + if (!NT_STATUS_IS_OK(status)) { + int eno = 0; + + eno = SMBC_errno(context, srv->cli); + file = smbc_getFunctionOpendir(context)(context, fname); + if (!file) errno = eno; + TALLOC_FREE(frame); + return file; + } + + errno = EINVAL; /* FIXME, correct errno ? */ + TALLOC_FREE(frame); + return NULL; +} + /* * Routine to create a file */ @@ -214,6 +436,21 @@ SMBC_creat_ctx(SMBCCTX *context, O_WRONLY | O_CREAT | O_TRUNC, mode); } +SMBCFILE * +SMBC_creat2_ctx(SMBCCTX *context, + const char *path, + mode_t mode, + unsigned long share_access) +{ + if (!context || !context->internal->initialized) { + errno = EINVAL; + return NULL; + } + + return SMBC_open2_ctx(context, path, + O_WRONLY | O_CREAT | O_TRUNC, mode, share_access); +} + /* * Routine to read() a file ... */ diff --git a/source3/libsmb/libsmb_setget.c b/source3/libsmb/libsmb_setget.c index 0a02346..e8348d1 100644 --- a/source3/libsmb/libsmb_setget.c +++ b/source3/libsmb/libsmb_setget.c @@ -621,6 +621,30 @@ smbc_setFunctionCreat(SMBCCTX *c, smbc_creat_fn fn) c->creat = fn; } +smbc_open2_fn +smbc_getFunctionOpen2(SMBCCTX *c) +{ + return c->open2; +} + +void +smbc_setFunctionOpen2(SMBCCTX *c, smbc_open2_fn fn) +{ + c->open2 = fn; +} + +smbc_creat2_fn +smbc_getFunctionCreat2(SMBCCTX *c) +{ + return c->creat2; +} + +void +smbc_setFunctionCreat2(SMBCCTX *c, smbc_creat2_fn fn) +{ + c->creat2 = fn; +} + smbc_read_fn smbc_getFunctionRead(SMBCCTX *c) { -- 1.7.10.4