summaryrefslogtreecommitdiffstats
path: root/source/smbd
diff options
context:
space:
mode:
Diffstat (limited to 'source/smbd')
-rw-r--r--source/smbd/blocking.c2
-rw-r--r--source/smbd/close.c34
-rw-r--r--source/smbd/conn.c2
-rw-r--r--source/smbd/connection.c8
-rw-r--r--source/smbd/dir.c3
-rw-r--r--source/smbd/dosmode.c13
-rw-r--r--source/smbd/error.c11
-rw-r--r--source/smbd/filename.c27
-rw-r--r--source/smbd/lanman.c4
-rw-r--r--source/smbd/mangle_hash.c6
-rw-r--r--source/smbd/msdfs.c4
-rw-r--r--source/smbd/negprot.c19
-rw-r--r--source/smbd/notify_hash.c2
-rw-r--r--source/smbd/nttrans.c236
-rw-r--r--source/smbd/open.c375
-rw-r--r--source/smbd/oplock.c80
-rw-r--r--source/smbd/posix_acls.c42
-rw-r--r--source/smbd/process.c257
-rw-r--r--source/smbd/quotas.c204
-rw-r--r--source/smbd/reply.c187
-rw-r--r--source/smbd/server.c10
-rw-r--r--source/smbd/service.c41
-rw-r--r--source/smbd/session.c4
-rw-r--r--source/smbd/sesssetup.c4
-rw-r--r--source/smbd/trans2.c77
-rw-r--r--source/smbd/uid.c7
-rw-r--r--source/smbd/utmp.c4
-rw-r--r--source/smbd/vfs.c36
28 files changed, 1442 insertions, 257 deletions
diff --git a/source/smbd/blocking.c b/source/smbd/blocking.c
index c0512d5539b..3983a4cbdfb 100644
--- a/source/smbd/blocking.c
+++ b/source/smbd/blocking.c
@@ -680,7 +680,7 @@ void process_blocking_lock_queue(time_t t)
continue;
}
- if(!set_current_service(conn,True)) {
+ if(!set_current_service(conn,SVAL(blr->inbuf,smb_flg),True)) {
DEBUG(0,("process_blocking_lock_queue: Unable to become service Error was %s.\n", strerror(errno) ));
/*
* Remove the entry and return an error to the client.
diff --git a/source/smbd/close.c b/source/smbd/close.c
index 8b3010c1b2e..6de27746442 100644
--- a/source/smbd/close.c
+++ b/source/smbd/close.c
@@ -2,6 +2,7 @@
Unix SMB/CIFS implementation.
file closing
Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Jeremy Allison 1992-2004.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -105,6 +106,36 @@ static int close_filestruct(files_struct *fsp)
}
/****************************************************************************
+ If any deferred opens are waiting on this close, notify them.
+****************************************************************************/
+
+static void notify_deferred_opens(files_struct *fsp)
+{
+ deferred_open_entry *de_array = NULL;
+ int num_de_entries, i;
+ pid_t mypid = sys_getpid();
+
+ if (!lp_defer_sharing_violations()) {
+ return;
+ }
+
+ num_de_entries = get_deferred_opens(fsp->conn, fsp->dev, fsp->inode, &de_array);
+ for (i = 0; i < num_de_entries; i++) {
+ deferred_open_entry *entry = &de_array[i];
+ if (entry->pid == mypid) {
+ /*
+ * We need to notify ourself to retry the open.
+ * Do this by finding the queued SMB record, moving it
+ * to the head of the queue and changing the wait time to zero.
+ */
+ schedule_sharing_violation_open_smb_message(entry->mid);
+ } else {
+ send_deferred_open_retry_message(entry);
+ }
+ }
+}
+
+/****************************************************************************
Close a file.
If normal_close is 1 then this came from a normal SMBclose (or equivalent)
@@ -177,6 +208,9 @@ static int close_normal_file(files_struct *fsp, BOOL normal_close)
SAFE_FREE(share_entry);
+ /* Notify any deferred opens waiting on this close. */
+ notify_deferred_opens(fsp);
+
/*
* NT can set delete_on_close of the last open
* reference to a file.
diff --git a/source/smbd/conn.c b/source/smbd/conn.c
index e083e144263..34e19a3ca6b 100644
--- a/source/smbd/conn.c
+++ b/source/smbd/conn.c
@@ -161,7 +161,7 @@ void conn_close_all(void)
connection_struct *conn, *next;
for (conn=Connections;conn;conn=next) {
next=conn->next;
- set_current_service(conn, True);
+ set_current_service(conn, 0, True);
close_cnum(conn, conn->vuid);
}
}
diff --git a/source/smbd/connection.c b/source/smbd/connection.c
index a9ab1424615..5bb76eb3bd8 100644
--- a/source/smbd/connection.c
+++ b/source/smbd/connection.c
@@ -29,8 +29,8 @@ static TDB_CONTEXT *tdb;
TDB_CONTEXT *conn_tdb_ctx(void)
{
if (!tdb)
- tdb = tdb_open_ex(lock_path("connections.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT,
- O_RDWR | O_CREAT, 0644, smbd_tdb_log);
+ tdb = tdb_open_log(lock_path("connections.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT,
+ O_RDWR | O_CREAT, 0644);
return tdb;
}
@@ -131,8 +131,8 @@ BOOL claim_connection(connection_struct *conn, const char *name,int max_connecti
TDB_DATA kbuf, dbuf;
if (!tdb)
- tdb = tdb_open_ex(lock_path("connections.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT,
- O_RDWR | O_CREAT, 0644, smbd_tdb_log);
+ tdb = tdb_open_log(lock_path("connections.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT,
+ O_RDWR | O_CREAT, 0644);
if (!tdb)
return False;
diff --git a/source/smbd/dir.c b/source/smbd/dir.c
index 06ef23ab8cd..b88f687766d 100644
--- a/source/smbd/dir.c
+++ b/source/smbd/dir.c
@@ -763,7 +763,8 @@ static BOOL user_can_write_file(connection_struct *conn, char *name, SMB_STRUCT_
return True;
else
fsp = open_file_shared1(conn, name, pst, FILE_WRITE_ATTRIBUTES, SET_DENY_MODE(DENY_NONE),
- (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &smb_action);
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, INTERNAL_OPEN_ONLY,
+ &access_mode, &smb_action);
if (!fsp)
return False;
diff --git a/source/smbd/dosmode.c b/source/smbd/dosmode.c
index d7dc63bb2fd..33c75fffd53 100644
--- a/source/smbd/dosmode.c
+++ b/source/smbd/dosmode.c
@@ -182,6 +182,7 @@ static BOOL get_ea_dos_attribute(connection_struct *conn, const char *path,SMB_S
if ((errno != ENOTSUP) && (errno != ENOATTR) && (errno != EACCES)) {
DEBUG(1,("get_ea_dos_attributes: Cannot get attribute from EA on file %s: Error = %s\n",
path, strerror(errno) ));
+ set_store_dos_attributes(SNUM(conn), False);
}
#endif
return False;
@@ -224,9 +225,21 @@ static BOOL set_ea_dos_attribute(connection_struct *conn, const char *path, SMB_
files_struct *fsp = NULL;
BOOL ret = False;
+ if (!lp_store_dos_attributes(SNUM(conn))) {
+ return False;
+ }
+
snprintf(attrstr, sizeof(attrstr)-1, "0x%x", dosmode & SAMBA_ATTRIBUTES_MASK);
if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == -1) {
if((errno != EPERM) && (errno != EACCES)) {
+ if (errno == ENOSYS
+#if defined(ENOTSUP)
+ || errno == ENOTSUP) {
+#else
+ ) {
+#endif
+ set_store_dos_attributes(SNUM(conn), False);
+ }
return False;
}
diff --git a/source/smbd/error.c b/source/smbd/error.c
index 9c81d465e7a..d611e0ef873 100644
--- a/source/smbd/error.c
+++ b/source/smbd/error.c
@@ -29,6 +29,17 @@ NTSTATUS unix_ERR_ntstatus = NT_STATUS_OK;
extern struct unix_error_map unix_dos_nt_errmap[];
/****************************************************************************
+ Ensure we don't have any errors cached.
+****************************************************************************/
+
+void clear_cached_errors(void)
+{
+ unix_ERR_class = SMB_SUCCESS;
+ unix_ERR_code = 0;
+ unix_ERR_ntstatus = NT_STATUS_OK;
+}
+
+/****************************************************************************
Create an error packet from a cached error.
****************************************************************************/
diff --git a/source/smbd/filename.c b/source/smbd/filename.c
index 5e5f5726913..ab75d9c06ae 100644
--- a/source/smbd/filename.c
+++ b/source/smbd/filename.c
@@ -135,7 +135,7 @@ BOOL unix_convert(pstring name,connection_struct *conn,char *saved_last_componen
if (SMB_VFS_STAT(conn,name,&st) == 0) {
*pst = st;
}
- DEBUG(5,("conversion finished %s -> %s\n",orig_path, name));
+ DEBUG(5,("conversion finished \"\" -> %s\n",name));
return(True);
}
@@ -237,6 +237,15 @@ BOOL unix_convert(pstring name,connection_struct *conn,char *saved_last_componen
*/
DEBUG(5,("Not a dir %s\n",start));
*end = '/';
+ /*
+ * We need to return the fact that the intermediate
+ * name resolution failed. This is used to return an
+ * error of ERRbadpath rather than ERRbadfile. Some
+ * Windows applications depend on the difference between
+ * these two errors.
+ */
+ errno = ENOTDIR;
+ *bad_path = True;
return(False);
}
@@ -265,6 +274,9 @@ BOOL unix_convert(pstring name,connection_struct *conn,char *saved_last_componen
if (end)
pstrcpy(rest,end+1);
+ /* Reset errno so we can detect directory open errors. */
+ errno = 0;
+
/*
* Try to find this part of the path in the directory.
*/
@@ -292,6 +304,11 @@ BOOL unix_convert(pstring name,connection_struct *conn,char *saved_last_componen
return(False);
}
+ if (errno == ENOTDIR) {
+ *bad_path = True;
+ return(False);
+ }
+
/*
* Just the last part of the name doesn't exist.
* We may need to strupper() or strlower() it in case
@@ -392,12 +409,11 @@ BOOL check_name(pstring name,connection_struct *conn)
{
BOOL ret = True;
- errno = 0;
-
if (IS_VETO_PATH(conn, name)) {
/* Is it not dot or dot dot. */
if (!((name[0] == '.') && (!name[1] || (name[1] == '.' && !name[2])))) {
DEBUG(5,("file path name %s vetoed\n",name));
+ errno = ENOENT;
return False;
}
}
@@ -416,13 +432,15 @@ BOOL check_name(pstring name,connection_struct *conn)
if ( (SMB_VFS_LSTAT(conn,name,&statbuf) != -1) &&
(S_ISLNK(statbuf.st_mode)) ) {
DEBUG(3,("check_name: denied: file path name %s is a symlink\n",name));
+ errno = EACCES;
ret = False;
}
}
#endif
- if (!ret)
+ if (!ret) {
DEBUG(5,("check_name on %s failed\n",name));
+ }
return(ret);
}
@@ -496,5 +514,6 @@ static BOOL scan_directory(const char *path, char *name, size_t maxlength,
}
CloseDir(cur_dir);
+ errno = ENOENT;
return(False);
}
diff --git a/source/smbd/lanman.c b/source/smbd/lanman.c
index c4df84e76c7..dd9708356e7 100644
--- a/source/smbd/lanman.c
+++ b/source/smbd/lanman.c
@@ -1121,11 +1121,11 @@ static int fill_srv_info(struct srv_info_struct *service,
switch (uLevel)
{
case 0:
- push_ascii(p,service->name, 15, STR_TERMINATE);
+ push_ascii(p,service->name, MAX_NETBIOSNAME_LEN, STR_TERMINATE);
break;
case 1:
- push_ascii(p,service->name,15, STR_TERMINATE);
+ push_ascii(p,service->name,MAX_NETBIOSNAME_LEN, STR_TERMINATE);
SIVAL(p,18,service->type);
SIVAL(p,22,PTR_DIFF(p2,baseaddr));
len += CopyAndAdvance(&p2,service->comment,&l2);
diff --git a/source/smbd/mangle_hash.c b/source/smbd/mangle_hash.c
index 7b8cbdbddba..d7239b82a79 100644
--- a/source/smbd/mangle_hash.c
+++ b/source/smbd/mangle_hash.c
@@ -546,8 +546,10 @@ static void cache_mangled_name( char *mangled_name, char *raw_name )
/* Fill the new cache entry, and add it to the cache. */
s1 = (char *)(new_entry + 1);
s2 = (char *)&(s1[mangled_len + 1]);
- safe_strcpy( s1, mangled_name, mangled_len );
- safe_strcpy( s2, raw_name, raw_len );
+ memcpy( s1, mangled_name, mangled_len );
+ s1[mangled_len] = '\0';
+ memcpy( s2, raw_name, raw_len );
+ s2[raw_len] = '\0';
ubi_cachePut( mangled_cache, i, new_entry, s1 );
}
diff --git a/source/smbd/msdfs.c b/source/smbd/msdfs.c
index c66f0477a84..6c132897f98 100644
--- a/source/smbd/msdfs.c
+++ b/source/smbd/msdfs.c
@@ -65,7 +65,7 @@ static BOOL parse_dfs_path(char* pathname, struct dfs_path* pdp)
DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
/* rest is reqpath */
- check_path_syntax(pdp->reqpath, p+1);
+ check_path_syntax(pdp->reqpath, p+1,True);
DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
return True;
@@ -111,7 +111,7 @@ static BOOL parse_processed_dfs_path(char* pathname, struct dfs_path* pdp)
DEBUG(10,("parse_processed_dfs_path: servicename: %s\n",pdp->servicename));
/* rest is reqpath */
- check_path_syntax(pdp->reqpath, p+1);
+ check_path_syntax(pdp->reqpath, p+1,True);
DEBUG(10,("parse_processed_dfs_path: rest of the path: %s\n",pdp->reqpath));
return True;
diff --git a/source/smbd/negprot.c b/source/smbd/negprot.c
index 1843c174bb5..5ff53f63007 100644
--- a/source/smbd/negprot.c
+++ b/source/smbd/negprot.c
@@ -401,8 +401,9 @@ protocol [LANMAN2.1]
#define ARCH_WIN2K 0xC /* Win2K is like NT */
#define ARCH_OS2 0x14 /* Again OS/2 is like NT */
#define ARCH_SAMBA 0x20
+#define ARCH_CIFSFS 0x40
-#define ARCH_ALL 0x3F
+#define ARCH_ALL 0x7F
/* List of supported protocols, most desired first */
static const struct {
@@ -413,6 +414,7 @@ static const struct {
} supported_protocols[] = {
{"NT LANMAN 1.0", "NT1", reply_nt1, PROTOCOL_NT1},
{"NT LM 0.12", "NT1", reply_nt1, PROTOCOL_NT1},
+ {"POSIX 2", "NT1", reply_nt1, PROTOCOL_NT1},
{"LM1.2X002", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
{"Samba", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
{"DOS LM1.2X002", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
@@ -460,7 +462,7 @@ int reply_negprot(connection_struct *conn,
else if (strcsequal(p,"DOS LANMAN2.1"))
arch &= ( ARCH_WFWG | ARCH_WIN95 );
else if (strcsequal(p,"NT LM 0.12"))
- arch &= ( ARCH_WIN95 | ARCH_WINNT | ARCH_WIN2K );
+ arch &= ( ARCH_WIN95 | ARCH_WINNT | ARCH_WIN2K | ARCH_CIFSFS);
else if (strcsequal(p,"LANMAN2.1"))
arch &= ( ARCH_WINNT | ARCH_WIN2K | ARCH_OS2 );
else if (strcsequal(p,"LM1.2X002"))
@@ -472,12 +474,23 @@ int reply_negprot(connection_struct *conn,
else if (strcsequal(p,"Samba")) {
arch = ARCH_SAMBA;
break;
+ } else if (strcsequal(p,"POSIX 2")) {
+ arch = ARCH_CIFSFS;
+ break;
}
p += strlen(p) + 2;
}
-
+
+ /* CIFSFS can send one arch only, NT LM 0.12. */
+ if (Index == 1 && (arch & ARCH_CIFSFS)) {
+ arch = ARCH_CIFSFS;
+ }
+
switch ( arch ) {
+ case ARCH_CIFSFS:
+ set_remote_arch(RA_CIFSFS);
+ break;
case ARCH_SAMBA:
set_remote_arch(RA_SAMBA);
break;
diff --git a/source/smbd/notify_hash.c b/source/smbd/notify_hash.c
index ec414454f9e..843580f6edf 100644
--- a/source/smbd/notify_hash.c
+++ b/source/smbd/notify_hash.c
@@ -164,7 +164,7 @@ static BOOL hash_check_notify(connection_struct *conn, uint16 vuid, char *path,
if (!change_to_user(conn,vuid))
return True;
- if (!set_current_service(conn,True)) {
+ if (!set_current_service(conn,FLAG_CASELESS_PATHNAMES,True)) {
change_to_root_user();
return True;
}
diff --git a/source/smbd/nttrans.c b/source/smbd/nttrans.c
index 26be4434fdb..f717efac63e 100644
--- a/source/smbd/nttrans.c
+++ b/source/smbd/nttrans.c
@@ -651,7 +651,7 @@ create_options = 0x%x root_dir_fid = 0x%x\n", flags, desired_access, file_attrib
if(!dir_fsp->is_directory) {
- srvstr_get_path(inbuf, fname, smb_buf(inbuf), sizeof(fname), 0, STR_TERMINATE, &status);
+ srvstr_get_path(inbuf, fname, smb_buf(inbuf), sizeof(fname), 0, STR_TERMINATE, &status,False);
if (!NT_STATUS_IS_OK(status)) {
END_PROFILE(SMBntcreateX);
return ERROR_NT(status);
@@ -693,14 +693,14 @@ create_options = 0x%x root_dir_fid = 0x%x\n", flags, desired_access, file_attrib
dir_name_len++;
}
- srvstr_get_path(inbuf, rel_fname, smb_buf(inbuf), sizeof(rel_fname), 0, STR_TERMINATE, &status);
+ srvstr_get_path(inbuf, rel_fname, smb_buf(inbuf), sizeof(rel_fname), 0, STR_TERMINATE, &status,False);
if (!NT_STATUS_IS_OK(status)) {
END_PROFILE(SMBntcreateX);
return ERROR_NT(status);
}
pstrcat(fname, rel_fname);
} else {
- srvstr_get_path(inbuf, fname, smb_buf(inbuf), sizeof(fname), 0, STR_TERMINATE, &status);
+ srvstr_get_path(inbuf, fname, smb_buf(inbuf), sizeof(fname), 0, STR_TERMINATE, &status,False);
if (!NT_STATUS_IS_OK(status)) {
END_PROFILE(SMBntcreateX);
return ERROR_NT(status);
@@ -762,7 +762,18 @@ create_options = 0x%x root_dir_fid = 0x%x\n", flags, desired_access, file_attrib
set_posix_case_semantics(conn, file_attributes);
unix_convert(fname,conn,0,&bad_path,&sbuf);
-
+ if (bad_path) {
+ restore_case_semantics(conn, file_attributes);
+ END_PROFILE(SMBntcreateX);
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ }
+ /* All file access must go through check_name() */
+ if (!check_name(fname,conn)) {
+ restore_case_semantics(conn, file_attributes);
+ END_PROFILE(SMBntcreateX);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
+ }
+
/*
* If it's a request for a directory open, deal with it separately.
*/
@@ -863,6 +874,11 @@ create_options = 0x%x root_dir_fid = 0x%x\n", flags, desired_access, file_attrib
restore_case_semantics(conn, file_attributes);
END_PROFILE(SMBntcreateX);
+ if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+ /* We have re-scheduled this call. */
+ clear_cached_errors();
+ return -1;
+ }
return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
}
}
@@ -1010,7 +1026,7 @@ static int do_nt_transact_create_pipe( connection_struct *conn, char *inbuf, cha
return ERROR_DOS(ERRDOS,ERRnoaccess);
}
- srvstr_get_path(inbuf, fname, params+53, sizeof(fname), parameter_count-53, STR_TERMINATE, &status);
+ srvstr_get_path(inbuf, fname, params+53, sizeof(fname), parameter_count-53, STR_TERMINATE, &status, False);
if (!NT_STATUS_IS_OK(status)) {
return ERROR_NT(status);
}
@@ -1213,7 +1229,7 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o
return ERROR_DOS(ERRDOS,ERRbadfid);
if(!dir_fsp->is_directory) {
- srvstr_get_path(inbuf, fname, params+53, sizeof(fname), parameter_count-53, STR_TERMINATE, &status);
+ srvstr_get_path(inbuf, fname, params+53, sizeof(fname), parameter_count-53, STR_TERMINATE, &status, False);
if (!NT_STATUS_IS_OK(status)) {
return ERROR_NT(status);
}
@@ -1246,14 +1262,14 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o
{
pstring tmpname;
- srvstr_get_path(inbuf, tmpname, params+53, sizeof(tmpname), parameter_count-53, STR_TERMINATE, &status);
+ srvstr_get_path(inbuf, tmpname, params+53, sizeof(tmpname), parameter_count-53, STR_TERMINATE, &status, False);
if (!NT_STATUS_IS_OK(status)) {
return ERROR_NT(status);
}
pstrcat(fname, tmpname);
}
} else {
- srvstr_get_path(inbuf, fname, params+53, sizeof(fname), parameter_count-53, STR_TERMINATE, &status);
+ srvstr_get_path(inbuf, fname, params+53, sizeof(fname), parameter_count-53, STR_TERMINATE, &status, False);
if (!NT_STATUS_IS_OK(status)) {
return ERROR_NT(status);
}
@@ -1287,6 +1303,15 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o
RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
unix_convert(fname,conn,0,&bad_path,&sbuf);
+ if (bad_path) {
+ restore_case_semantics(conn, file_attributes);
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ }
+ /* All file access must go through check_name() */
+ if (!check_name(fname,conn)) {
+ restore_case_semantics(conn, file_attributes);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
+ }
/*
* If it's a request for a directory open, deal with it separately.
@@ -1347,6 +1372,11 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o
}
} else {
restore_case_semantics(conn, file_attributes);
+ if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+ /* We have re-scheduled this call. */
+ clear_cached_errors();
+ return -1;
+ }
return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
}
}
@@ -1491,6 +1521,155 @@ int reply_ntcancel(connection_struct *conn,
}
/****************************************************************************
+ Copy a file.
+****************************************************************************/
+
+static NTSTATUS copy_internals(connection_struct *conn, char *oldname, char *newname, uint16 attrs)
+{
+ BOOL bad_path_oldname = False;
+ BOOL bad_path_newname = False;
+ SMB_STRUCT_STAT sbuf1, sbuf2;
+ pstring last_component_oldname;
+ pstring last_component_newname;
+ files_struct *fsp1,*fsp2;
+ uint16 fmode;
+ int access_mode;
+ int smb_action;
+ SMB_OFF_T ret=-1;
+ int close_ret;
+ NTSTATUS status = NT_STATUS_OK;
+
+ ZERO_STRUCT(sbuf1);
+ ZERO_STRUCT(sbuf2);
+
+ /* No wildcards. */
+ if (ms_has_wild(newname) || ms_has_wild(oldname)) {
+ return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+
+ if (!CAN_WRITE(conn))
+ return NT_STATUS_MEDIA_WRITE_PROTECTED;
+
+ unix_convert(oldname,conn,last_component_oldname,&bad_path_oldname,&sbuf1);
+ if (bad_path_oldname) {
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+
+ /* Quick check for "." and ".." */
+ if (last_component_oldname[0] == '.') {
+ if (!last_component_oldname[1] || (last_component_oldname[1] == '.' && !last_component_oldname[2])) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ }
+
+ /* Source must already exist. */
+ if (!VALID_STAT(sbuf1)) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ if (!check_name(oldname,conn)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* Ensure attributes match. */
+ fmode = dos_mode(conn,oldname,&sbuf1);
+ if ((fmode & ~attrs) & (aHIDDEN | aSYSTEM))
+ return NT_STATUS_NO_SUCH_FILE;
+
+ unix_convert(newname,conn,last_component_newname,&bad_path_newname,&sbuf2);
+ if (bad_path_newname) {
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+
+ /* Quick check for "." and ".." */
+ if (last_component_newname[0] == '.') {
+ if (!last_component_newname[1] || (last_component_newname[1] == '.' && !last_component_newname[2])) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ }
+
+ /* Disallow if newname already exists. */
+ if (VALID_STAT(sbuf2)) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ if (!check_name(newname,conn)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* No links from a directory. */
+ if (S_ISDIR(sbuf1.st_mode)) {
+ return NT_STATUS_FILE_IS_A_DIRECTORY;
+ }
+
+ /* Ensure this is within the share. */
+ if (!reduce_name(conn, oldname) != 0) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ DEBUG(10,("copy_internals: doing file copy %s to %s\n", oldname, newname));
+
+ fsp1 = open_file_shared1(conn,oldname,&sbuf1,FILE_READ_DATA,SET_DENY_MODE(DENY_ALL)|SET_OPEN_MODE(DOS_OPEN_RDONLY),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),FILE_ATTRIBUTE_NORMAL,0,
+ &access_mode,&smb_action);
+
+ if (!fsp1) {
+ status = NT_STATUS_ACCESS_DENIED;
+ if (unix_ERR_class == ERRDOS && unix_ERR_code == ERRbadshare)
+ status = NT_STATUS_SHARING_VIOLATION;
+ unix_ERR_class = 0;
+ unix_ERR_code = 0;
+ unix_ERR_ntstatus = NT_STATUS_OK;
+ return status;
+ }
+
+ fsp2 = open_file_shared1(conn,newname,&sbuf2,FILE_WRITE_DATA,SET_DENY_MODE(DENY_ALL)|SET_OPEN_MODE(DOS_OPEN_WRONLY),
+ (FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_FAIL),fmode,INTERNAL_OPEN_ONLY,
+ &access_mode,&smb_action);
+
+ if (!fsp2) {
+ status = NT_STATUS_ACCESS_DENIED;
+ if (unix_ERR_class == ERRDOS && unix_ERR_code == ERRbadshare)
+ status = NT_STATUS_SHARING_VIOLATION;
+ unix_ERR_class = 0;
+ unix_ERR_code = 0;
+ unix_ERR_ntstatus = NT_STATUS_OK;
+ close_file(fsp1,False);
+ return status;
+ }
+
+ if (sbuf1.st_size)
+ ret = vfs_transfer_file(fsp1, fsp2, sbuf1.st_size);
+
+ /*
+ * As we are opening fsp1 read-only we only expect
+ * an error on close on fsp2 if we are out of space.
+ * Thus we don't look at the error return from the
+ * close of fsp1.
+ */
+ close_file(fsp1,False);
+
+ /* Ensure the modtime is set correctly on the destination file. */
+ fsp2->pending_modtime = sbuf1.st_mtime;
+
+ close_ret = close_file(fsp2,False);
+
+ /* Grrr. We have to do this as open_file_shared1 adds aARCH when it
+ creates the file. This isn't the correct thing to do in the copy case. JRA */
+ file_set_dosmode(conn, newname, fmode, &sbuf2);
+
+ if (ret < (SMB_OFF_T)sbuf1.st_size) {
+ return NT_STATUS_DISK_FULL;
+ }
+
+ if (close_ret != 0) {
+ status = map_nt_error_from_unix(close_ret);
+ DEBUG(3,("copy_internals: Error %s copy file %s to %s\n",
+ nt_errstr(status), oldname, newname));
+ }
+ return status;
+}
+
+/****************************************************************************
Reply to a NT rename request.
****************************************************************************/
@@ -1507,13 +1686,8 @@ int reply_ntrename(connection_struct *conn,
START_PROFILE(SMBntrename);
- if ((rename_type != RENAME_FLAG_RENAME) && (rename_type != RENAME_FLAG_HARD_LINK)) {
- END_PROFILE(SMBntrename);
- return ERROR_NT(NT_STATUS_ACCESS_DENIED);
- }
-
p = smb_buf(inbuf) + 1;
- p += srvstr_get_path(inbuf, oldname, p, sizeof(oldname), 0, STR_TERMINATE, &status);
+ p += srvstr_get_path(inbuf, oldname, p, sizeof(oldname), 0, STR_TERMINATE, &status, True);
if (!NT_STATUS_IS_OK(status)) {
END_PROFILE(SMBntrename);
return ERROR_NT(status);
@@ -1525,8 +1699,13 @@ int reply_ntrename(connection_struct *conn,
return ERROR_NT(NT_STATUS_ACCESS_DENIED);
}
+ if (ms_has_wild(oldname)) {
+ END_PROFILE(SMBntrename);
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_SYNTAX_BAD);
+ }
+
p++;
- p += srvstr_get_path(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status);
+ p += srvstr_get_path(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status, False);
if (!NT_STATUS_IS_OK(status)) {
END_PROFILE(SMBntrename);
return ERROR_NT(status);
@@ -1537,14 +1716,31 @@ int reply_ntrename(connection_struct *conn,
DEBUG(3,("reply_ntrename : %s -> %s\n",oldname,newname));
- if (rename_type == RENAME_FLAG_RENAME) {
- status = rename_internals(conn, oldname, newname, attrs, False);
- } else {
- status = hardlink_internals(conn, oldname, newname);
+ switch(rename_type) {
+ case RENAME_FLAG_RENAME:
+ status = rename_internals(conn, oldname, newname, attrs, False);
+ break;
+ case RENAME_FLAG_HARD_LINK:
+ status = hardlink_internals(conn, oldname, newname);
+ break;
+ case RENAME_FLAG_COPY:
+ status = copy_internals(conn, oldname, newname, attrs);
+ break;
+ case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
+ status = NT_STATUS_INVALID_PARAMETER;
+ break;
+ default:
+ status = NT_STATUS_ACCESS_DENIED; /* Default error. */
+ break;
}
if (!NT_STATUS_IS_OK(status)) {
END_PROFILE(SMBntrename);
+ if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+ /* We have re-scheduled this call. */
+ clear_cached_errors();
+ return -1;
+ }
return ERROR_NT(status);
}
@@ -1630,7 +1826,7 @@ static int call_nt_transact_rename(connection_struct *conn, char *inbuf, char *o
fsp = file_fsp(params, 0);
replace_if_exists = (SVAL(params,2) & RENAME_REPLACE_IF_EXISTS) ? True : False;
CHECK_FSP(fsp, conn);
- srvstr_get_path(inbuf, new_name, params+4, sizeof(new_name), -1, STR_TERMINATE, &status);
+ srvstr_get_path(inbuf, new_name, params+4, sizeof(new_name), -1, STR_TERMINATE, &status, True);
if (!NT_STATUS_IS_OK(status)) {
return ERROR_NT(status);
}
diff --git a/source/smbd/open.c b/source/smbd/open.c
index 3b4f50b0656..65500c65cee 100644
--- a/source/smbd/open.c
+++ b/source/smbd/open.c
@@ -2,7 +2,7 @@
Unix SMB/CIFS implementation.
file opening and share modes
Copyright (C) Andrew Tridgell 1992-1998
- Copyright (C) Jeremy Allison 2001
+ Copyright (C) Jeremy Allison 2001-2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -26,6 +26,11 @@ extern uint16 global_oplock_port;
extern uint16 global_smbpid;
extern BOOL global_client_failed_oplock_break;
+struct dev_inode_bundle {
+ SMB_DEV_T dev;
+ SMB_INO_T inode;
+};
+
/****************************************************************************
fd support routines - attempt to do a dos_open.
****************************************************************************/
@@ -239,11 +244,12 @@ static BOOL open_file(files_struct *fsp,connection_struct *conn,
BOOLSTR(fsp->can_read), BOOLSTR(fsp->can_write),
conn->num_files_open + 1));
+ errno = 0;
return True;
}
/*******************************************************************
-return True if the filename is one of the special executable types
+ Return True if the filename is one of the special executable types.
********************************************************************/
static BOOL is_executable(const char *fname)
@@ -262,12 +268,13 @@ static BOOL is_executable(const char *fname)
enum {AFAIL,AREAD,AWRITE,AALL};
/*******************************************************************
-reproduce the share mode access table
-this is horrendoously complex, and really can't be justified on any
-rational grounds except that this is _exactly_ what NT does. See
-the DENY1 and DENY2 tests in smbtorture for a comprehensive set of
-test routines.
+ Reproduce the share mode access table.
+ This is horrendoously complex, and really can't be justified on any
+ rational grounds except that this is _exactly_ what NT does. See
+ the DENY1 and DENY2 tests in smbtorture for a comprehensive set of
+ test routines.
********************************************************************/
+
static int access_table(int new_deny,int old_deny,int old_mode,
BOOL same_pid, BOOL isexe)
{
@@ -353,9 +360,8 @@ static int access_table(int new_deny,int old_deny,int old_mode,
return(AFAIL);
}
-
/****************************************************************************
-check if we can open a file with a share mode
+ Check if we can open a file with a share mode.
****************************************************************************/
static BOOL check_share_mode(connection_struct *conn, share_mode_entry *share, int share_mode, uint32 desired_access,
@@ -364,6 +370,8 @@ static BOOL check_share_mode(connection_struct *conn, share_mode_entry *share, i
int deny_mode = GET_DENY_MODE(share_mode);
int old_open_mode = GET_OPEN_MODE(share->share_mode);
int old_deny_mode = GET_DENY_MODE(share->share_mode);
+ BOOL non_io_open_request;
+ BOOL non_io_open_existing;
/*
* share modes = false means don't bother to check for
@@ -373,6 +381,18 @@ static BOOL check_share_mode(connection_struct *conn, share_mode_entry *share, i
if(!lp_share_modes(SNUM(conn)))
return True;
+ if (desired_access & ~(SYNCHRONIZE_ACCESS|READ_CONTROL_ACCESS|FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES)) {
+ non_io_open_request = False;
+ } else {
+ non_io_open_request = True;
+ }
+
+ if (share->desired_access & ~(SYNCHRONIZE_ACCESS|READ_CONTROL_ACCESS|FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES)) {
+ non_io_open_existing = False;
+ } else {
+ non_io_open_existing = True;
+ }
+
/*
* Don't allow any opens once the delete on close flag has been
* set.
@@ -411,8 +431,7 @@ static BOOL check_share_mode(connection_struct *conn, share_mode_entry *share, i
* and the existing desired_acces then share modes don't conflict.
*/
- if ( !(desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) &&
- !(share->desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) ) {
+ if (non_io_open_request && non_io_open_existing) {
/*
* Wrinkle discovered by smbtorture....
@@ -436,6 +455,13 @@ static BOOL check_share_mode(connection_struct *conn, share_mode_entry *share, i
and existing desired access (0x%x) are non-data opens\n",
fname, (unsigned int)desired_access, (unsigned int)share->desired_access ));
return True;
+ } else if (non_io_open_request || non_io_open_existing) {
+ /*
+ * If either are non-io opens then share modes don't conflict.
+ */
+ DEBUG(5,("check_share_mode: One non-io open. Allowing open on file %s as desired access (0x%x) doesn't conflict with\
+existing desired access (0x%x).\n", fname, (unsigned int)desired_access, (unsigned int)share->desired_access ));
+ return True;
}
/*
@@ -537,6 +563,20 @@ static void validate_my_share_entries(int num, share_mode_entry *share_entry)
}
#endif
+struct share_mode_entry_list {
+ struct share_mode_entry_list *next, *prev;
+ share_mode_entry entry;
+};
+
+static void free_broken_entry_list(struct share_mode_entry_list *broken_entry_list)
+{
+ while (broken_entry_list) {
+ struct share_mode_entry_list *broken_entry = broken_entry_list;
+ DLIST_REMOVE(broken_entry_list, broken_entry);
+ SAFE_FREE(broken_entry);
+ }
+}
+
/****************************************************************************
Deal with open deny mode and oplock break processing.
Invarient: Share mode must be locked on entry and exit.
@@ -554,8 +594,8 @@ static int open_mode_check(connection_struct *conn, const char *fname, SMB_DEV_T
int oplock_contention_count = 0;
share_mode_entry *old_shares = 0;
BOOL fcbopen = False;
- BOOL broke_oplock;
-
+ BOOL broke_oplock;
+
if(GET_OPEN_MODE(share_mode) == DOS_OPEN_FCB)
fcbopen = True;
@@ -564,17 +604,25 @@ static int open_mode_check(connection_struct *conn, const char *fname, SMB_DEV_T
if(num_share_modes == 0)
return 0;
+ if (desired_access && ((desired_access & ~(SYNCHRONIZE_ACCESS|FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES))==0) &&
+ ((desired_access & (SYNCHRONIZE_ACCESS|FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES)) != 0)) {
+ /* Stat open that doesn't trigger oplock breaks or share mode checks... ! JRA. */
+ return num_share_modes;
+ }
+
/*
* Check if the share modes will give us access.
*/
do {
- share_mode_entry broken_entry;
-
+ struct share_mode_entry_list *broken_entry_list = NULL;
+ struct share_mode_entry_list *broken_entry = NULL;
+
broke_oplock = False;
*p_all_current_opens_are_level_II = True;
for(i = 0; i < num_share_modes; i++) {
+ BOOL cause_oplock_break = False;
share_mode_entry *share_entry = &old_shares[i];
#if defined(DEVELOPER)
@@ -589,9 +637,17 @@ static int open_mode_check(connection_struct *conn, const char *fname, SMB_DEV_T
* it before continuing.
*/
- if((*p_oplock_request && EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type)) ||
+ /* Was this a delete this file request ? */
+ if (!*p_oplock_request && desired_access == DELETE_ACCESS &&
+ !BATCH_OPLOCK_TYPE(share_entry->op_type)) {
+ /* Don't break the oplock in this case. */
+ cause_oplock_break = False;
+ } else if((*p_oplock_request && EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type)) ||
(!*p_oplock_request && (share_entry->op_type != NO_OPLOCK))) {
-
+ cause_oplock_break = True;
+ }
+
+ if(cause_oplock_break) {
BOOL opb_ret;
DEBUG(5,("open_mode_check: oplock_request = %d, breaking oplock (%x) on file %s, \
@@ -622,50 +678,65 @@ dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (dou
return -1;
}
+ broken_entry = malloc(sizeof(struct share_mode_entry_list));
+ if (!broken_entry) {
+ smb_panic("open_mode_check: malloc fail.\n");
+ }
+ broken_entry->entry = *share_entry;
+ DLIST_ADD(broken_entry_list, broken_entry);
broke_oplock = True;
- broken_entry = *share_entry;
- break;
} else if (!LEVEL_II_OPLOCK_TYPE(share_entry->op_type)) {
*p_all_current_opens_are_level_II = False;
}
-
+ } /* end for */
+
+ if (broke_oplock) {
+ /* Update the current open table. */
+ SAFE_FREE(old_shares);
+ num_share_modes = get_share_modes(conn, dev, inode, &old_shares);
+ }
+
+ /* Now we check the share modes, after any oplock breaks. */
+ for(i = 0; i < num_share_modes; i++) {
+ share_mode_entry *share_entry = &old_shares[i];
+
/* someone else has a share lock on it, check to see if we can too */
if (!check_share_mode(conn, share_entry, share_mode, desired_access,
fname, fcbopen, p_flags)) {
SAFE_FREE(old_shares);
+ free_broken_entry_list(broken_entry_list);
errno = EACCES;
return -1;
}
-
- } /* end for */
-
- if(broke_oplock) {
- SAFE_FREE(old_shares);
- num_share_modes = get_share_modes(conn, dev, inode, &old_shares);
+ }
+
+ for(broken_entry = broken_entry_list; broken_entry; broken_entry = broken_entry->next) {
oplock_contention_count++;
/* Paranoia check that this is no longer an exlusive entry. */
for(i = 0; i < num_share_modes; i++) {
share_mode_entry *share_entry = &old_shares[i];
- if (share_modes_identical(&broken_entry, share_entry) &&
- EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type) ) {
+ if (share_modes_identical(&broken_entry->entry, share_entry) &&
+ EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type) ) {
/*
* This should not happen. The target left this oplock
* as exlusive.... The process *must* be dead....
*/
- DEBUG(0,("open_mode_check: exlusive oplock left by process %d after break ! For file %s, \
-dev = %x, inode = %.0f. Deleting it to continue...\n", (int)broken_entry.pid, fname, (unsigned int)dev, (double)inode));
+ DEBUG(0,("open_mode_check: exlusive oplock left by process %d \
+after break ! For file %s, dev = %x, inode = %.0f. Deleting it to continue...\n",
+ (int)broken_entry->entry.pid, fname, (unsigned int)dev, (double)inode));
- if (process_exists(broken_entry.pid)) {
+ if (process_exists(broken_entry->entry.pid)) {
DEBUG(0,("open_mode_check: Existent process %lu left active oplock.\n",
- (unsigned long)broken_entry.pid ));
+ (unsigned long)broken_entry->entry.pid ));
}
- if (del_share_entry(dev, inode, &broken_entry, NULL) == -1) {
+ if (del_share_entry(dev, inode, &broken_entry->entry, NULL) == -1) {
+ free_broken_entry_list(broken_entry_list);
errno = EACCES;
unix_ERR_class = ERRDOS;
unix_ERR_code = ERRbadshare;
@@ -683,8 +754,8 @@ dev = %x, inode = %.0f. Deleting it to continue...\n", (int)broken_entry.pid, fn
break;
}
} /* end for paranoia... */
- } /* end if broke_oplock */
-
+ } /* end for broken_entry */
+ free_broken_entry_list(broken_entry_list);
} while(broke_oplock);
if(old_shares != 0)
@@ -705,9 +776,119 @@ dev = %x, inode = %.0f. Deleting it to continue...\n", (int)broken_entry.pid, fn
}
/****************************************************************************
-set a kernel flock on a file for NFS interoperability
-this requires a patch to Linux
+ Delete the record for a handled deferred open entry.
****************************************************************************/
+
+static void delete_defered_open_entry_record(connection_struct *conn, SMB_DEV_T dev, SMB_INO_T inode)
+{
+ uint16 mid = get_current_mid();
+ pid_t mypid = sys_getpid();
+ deferred_open_entry *de_array = NULL;
+ int num_de_entries, i;
+
+ if (!lp_defer_sharing_violations()) {
+ return;
+ }
+
+ num_de_entries = get_deferred_opens(conn, dev, inode, &de_array);
+ for (i = 0; i < num_de_entries; i++) {
+ deferred_open_entry *entry = &de_array[i];
+ if (entry->pid == mypid && entry->mid == mid && entry->dev == dev &&
+ entry->inode == inode) {
+
+ /* Remove the deferred open entry from the array. */
+ delete_deferred_open_entry(entry);
+ SAFE_FREE(de_array);
+ return;
+ }
+ }
+ SAFE_FREE(de_array);
+}
+
+/****************************************************************************
+ Handle the 1 second delay in returning a SHARING_VIOLATION error.
+****************************************************************************/
+
+void defer_open_sharing_error(connection_struct *conn, struct timeval *ptv,
+ char *fname, SMB_DEV_T dev, SMB_INO_T inode)
+{
+ uint16 mid = get_current_mid();
+ pid_t mypid = sys_getpid();
+ deferred_open_entry *de_array = NULL;
+ int num_de_entries, i;
+ struct dev_inode_bundle dib;
+
+ if (!lp_defer_sharing_violations()) {
+ return;
+ }
+
+ dib.dev = dev;
+ dib.inode = inode;
+
+ num_de_entries = get_deferred_opens(conn, dev, inode, &de_array);
+ for (i = 0; i < num_de_entries; i++) {
+ deferred_open_entry *entry = &de_array[i];
+ if (entry->pid == mypid && entry->mid == mid) {
+ /*
+ * Check if a 1 second timeout has expired.
+ */
+ if (usec_time_diff(ptv, &entry->time) > SHARING_VIOLATION_USEC_WAIT) {
+ DEBUG(10,("defer_open_sharing_error: Deleting deferred open entry for mid %u, \
+file %s\n",
+ (unsigned int)mid, fname ));
+
+ /* Expired, return a real error. */
+ /* Remove the deferred open entry from the array. */
+
+ delete_deferred_open_entry(entry);
+ SAFE_FREE(de_array);
+ return;
+ }
+ /*
+ * If the timeout hasn't expired yet and we still have a sharing violation,
+ * just leave the entry in the deferred open array alone. We do need to
+ * reschedule this open call though (with the original created time).
+ */
+ DEBUG(10,("defer_open_sharing_error: time [%u.%06u] updating \
+deferred open entry for mid %u, file %s\n",
+ (unsigned int)entry->time.tv_sec,
+ (unsigned int)entry->time.tv_usec,
+ (unsigned int)mid, fname ));
+
+ push_sharing_violation_open_smb_message(&entry->time, (char *)&dib, sizeof(dib));
+ SAFE_FREE(de_array);
+ return;
+ }
+ }
+
+ DEBUG(10,("defer_open_sharing_error: time [%u.%06u] adding deferred open entry for mid %u, file %s\n",
+ (unsigned int)ptv->tv_sec, (unsigned int)ptv->tv_usec, (unsigned int)mid, fname ));
+
+ if (!push_sharing_violation_open_smb_message(ptv, (char *)&dib, sizeof(dib))) {
+ SAFE_FREE(de_array);
+ return;
+ }
+ if (!add_deferred_open(mid, ptv, dev, inode, global_oplock_port, fname)) {
+ remove_sharing_violation_open_smb_message(mid);
+ }
+
+ /*
+ * Push the MID of this packet on the signing queue.
+ * We only do this once, the first time we push the packet
+ * onto the deferred open queue, as this has a side effect
+ * of incrementing the response sequence number.
+ */
+
+ srv_defer_sign_response(mid);
+
+ SAFE_FREE(de_array);
+}
+
+/****************************************************************************
+ Set a kernel flock on a file for NFS interoperability.
+ This requires a patch to Linux.
+****************************************************************************/
+
static void kernel_flock(files_struct *fsp, int deny_mode)
{
#if HAVE_KERNEL_SHARE_MODES
@@ -781,6 +962,8 @@ files_struct *open_file_shared1(connection_struct *conn,char *fname, SMB_STRUCT_
BOOL file_existed = VALID_STAT(*psbuf);
BOOL fcbopen = False;
BOOL def_acl = False;
+ BOOL add_share_mode = True;
+ BOOL internal_only_open = False;
SMB_DEV_T dev = 0;
SMB_INO_T inode = 0;
int num_share_modes = 0;
@@ -792,9 +975,50 @@ files_struct *open_file_shared1(connection_struct *conn,char *fname, SMB_STRUCT_
mode_t new_mode = (mode_t)0;
int action;
uint32 existing_dos_mode = 0;
+ struct pending_message_list *pml = NULL;
+ uint16 mid = get_current_mid();
/* We add aARCH to this as this mode is only used if the file is created new. */
mode_t mode = unix_mode(conn,new_dos_mode | aARCH,fname);
+ if (oplock_request == INTERNAL_OPEN_ONLY) {
+ internal_only_open = True;
+ oplock_request = 0;
+ }
+
+ if ((pml = get_open_deferred_message(mid)) != NULL) {
+ struct dev_inode_bundle dib;
+
+ memcpy(&dib, pml->private_data.data, sizeof(dib));
+
+ /* There could be a race condition where the dev/inode pair
+ has changed since we deferred the message. If so, just
+ remove the deferred open entry and return sharing violation. */
+
+ /* If the timeout value is non-zero, we need to just
+ return sharing violation. Don't retry the open
+ as we were not notified of a close and we don't want to
+ trigger another spurious oplock break. */
+
+ if (!file_existed || dib.dev != psbuf->st_dev || dib.inode != psbuf->st_ino ||
+ pml->msg_time.tv_sec || pml->msg_time.tv_usec) {
+ /* Ensure we don't reprocess this message. */
+ remove_sharing_violation_open_smb_message(mid);
+
+ /* Now remove the deferred open entry under lock. */
+ lock_share_entry(conn, dib.dev, dib.inode);
+ delete_defered_open_entry_record(conn, dib.dev, dib.inode);
+ unlock_share_entry(conn, dib.dev, dib.inode);
+
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadshare;
+ unix_ERR_ntstatus = NT_STATUS_SHARING_VIOLATION;
+ return NULL;
+ }
+ /* Ensure we don't reprocess this message. */
+ remove_sharing_violation_open_smb_message(mid);
+
+ }
+
if (conn->printer) {
/* printers are handled completely differently. Most of the passed parameters are
ignored */
@@ -805,25 +1029,6 @@ files_struct *open_file_shared1(connection_struct *conn,char *fname, SMB_STRUCT_
return print_fsp_open(conn, fname);
}
- if (desired_access && ((desired_access & ~(SYNCHRONIZE_ACCESS|FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES))==0) &&
- ((desired_access & (SYNCHRONIZE_ACCESS|FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES)) != 0)) {
- /* Stat open that doesn't trigger oplock breaks or share mode checks... ! JRA. */
- oplock_request = 0;
- fsp = open_file_stat(conn, fname, psbuf);
- if (!fsp)
- return NULL;
-
- fsp->desired_access = desired_access;
- if (Access)
- *Access = DOS_OPEN_RDONLY;
- if (paction)
- *paction = FILE_WAS_OPENED;
-
- DEBUG(10,("open_file_shared: stat open for fname = %s share_mode = %x\n",
- fname, share_mode ));
- return fsp;
- }
-
fsp = file_new(conn);
if(!fsp)
return NULL;
@@ -947,6 +1152,17 @@ files_struct *open_file_shared1(connection_struct *conn,char *fname, SMB_STRUCT_
return NULL;
}
+ if (desired_access && ((desired_access & ~(SYNCHRONIZE_ACCESS|FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES))==0) &&
+ ((desired_access & (SYNCHRONIZE_ACCESS|FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES)) != 0)) {
+ /* Stat open that doesn't trigger oplock breaks or share mode checks... ! JRA. */
+ deny_mode = DENY_NONE;
+ if (file_existed) {
+ oplock_request = 0;
+ add_share_mode = False;
+ flags2 &= ~O_CREAT;
+ }
+ }
+
if (file_existed) {
dev = psbuf->st_dev;
@@ -985,17 +1201,28 @@ flags=0x%X flags2=0x%X mode=0%o returned %d\n",
unix_ERR_ntstatus = NT_STATUS_ACCESS_DENIED;
}
+ /*
+ * If we're returning a share violation, ensure we cope with
+ * the braindead 1 second delay.
+ */
+
+ if (!internal_only_open && NT_STATUS_EQUAL(unix_ERR_ntstatus,NT_STATUS_SHARING_VIOLATION)) {
+ /* The fsp->open_time here represents the current time of day. */
+ defer_open_sharing_error(conn, &fsp->open_time, fname, dev, inode);
+ }
+
unlock_share_entry(conn, dev, inode);
- if (fsp_open)
+ if (fsp_open) {
fd_close(conn, fsp);
+ /*
+ * We have detected a sharing violation here
+ * so return the correct error code
+ */
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadshare;
+ unix_ERR_ntstatus = NT_STATUS_SHARING_VIOLATION;
+ }
file_free(fsp);
- /*
- * We have detected a sharing violation here
- * so return the correct error code
- */
- unix_ERR_class = ERRDOS;
- unix_ERR_code = ERRbadshare;
- unix_ERR_ntstatus = NT_STATUS_SHARING_VIOLATION;
return NULL;
}
@@ -1060,6 +1287,16 @@ flags=0x%X flags2=0x%X mode=0%o returned %d\n",
&flags, &oplock_request, &all_current_opens_are_level_II);
if(num_share_modes == -1) {
+ /*
+ * If we're returning a share violation, ensure we cope with
+ * the braindead 1 second delay.
+ */
+
+ if (!internal_only_open && NT_STATUS_EQUAL(unix_ERR_ntstatus,NT_STATUS_SHARING_VIOLATION)) {
+ /* The fsp->open_time here represents the current time of day. */
+ defer_open_sharing_error(conn, &fsp->open_time, fname, dev, inode);
+ }
+
unlock_share_entry_fsp(fsp);
fd_close(conn,fsp);
file_free(fsp);
@@ -1166,14 +1403,18 @@ flags=0x%X flags2=0x%X mode=0%o returned %d\n",
oplock_request = 0;
}
- set_share_mode(fsp, port, oplock_request);
+ if (add_share_mode) {
+ set_share_mode(fsp, port, oplock_request);
+ }
if (delete_on_close) {
NTSTATUS result = set_delete_on_close_internal(fsp, delete_on_close);
if (NT_STATUS_V(result) != NT_STATUS_V(NT_STATUS_OK)) {
/* Remember to delete the mode we just added. */
- del_share_mode(fsp, NULL);
+ if (add_share_mode) {
+ del_share_mode(fsp, NULL);
+ }
unlock_share_entry_fsp(fsp);
fd_close(conn,fsp);
file_free(fsp);
@@ -1224,6 +1465,8 @@ flags=0x%X flags2=0x%X mode=0%o returned %d\n",
fname, (int)new_mode));
}
+ /* If this is a successful open, we must remove any deferred open records. */
+ delete_defered_open_entry_record(conn, fsp->dev, fsp->inode);
unlock_share_entry_fsp(fsp);
conn->num_files_open++;
diff --git a/source/smbd/oplock.c b/source/smbd/oplock.c
index 19e6956d9ef..1ffc798b1fc 100644
--- a/source/smbd/oplock.c
+++ b/source/smbd/oplock.c
@@ -388,6 +388,30 @@ pid %d, port %d, dev = %x, inode = %.0f, file_id = %lu\n",
(int)remotepid, from_port, (unsigned int)dev, (double)inode, file_id));
break;
+ case RETRY_DEFERRED_OPEN_CMD:
+
+ /* Request to retry and open that would return SHARING_VIOLATION. */
+ if (msg_len != DEFERRED_OPEN_MSG_LEN) {
+ DEBUG(0,("process_local_message: incorrect length for RETRY_DEFERRED_OPEN_CMD (was %d, should be %d).\n",
+ (int)msg_len, (int)DEFERRED_OPEN_MSG_LEN));
+ return False;
+ }
+ {
+ uint16 mid;
+
+ memcpy((char *)&remotepid, msg_start+DEFERRED_OPEN_PID_OFFSET,sizeof(remotepid));
+ memcpy((char *)&inode, msg_start+DEFERRED_OPEN_INODE_OFFSET,sizeof(inode));
+ memcpy((char *)&dev, msg_start+DEFERRED_OPEN_DEV_OFFSET,sizeof(dev));
+ memcpy((char *)&mid, msg_start+DEFERRED_OPEN_MID_OFFSET,sizeof(mid));
+
+ DEBUG(5,("process_local_message: RETRY_DEFERRED_OPEN from \
+pid %d, port %d, dev = %x, inode = %.0f, mid = %u\n",
+ (int)remotepid, from_port, (unsigned int)dev, (double)inode, (unsigned int)mid));
+
+ schedule_sharing_violation_open_smb_message(mid);
+ }
+ return True;
+
/*
* Keep this as a debug case - eventually we can remove it.
*/
@@ -592,6 +616,8 @@ BOOL oplock_break_level2(files_struct *fsp, BOOL local_request, int token)
*/
if (global_client_caps & CAP_LEVEL_II_OPLOCKS) {
+ BOOL sign_state;
+
/*
* If we are sending an oplock break due to an SMB sent
* by our own client we ensure that we wait at leat
@@ -603,10 +629,16 @@ BOOL oplock_break_level2(files_struct *fsp, BOOL local_request, int token)
wait_before_sending_break(local_request);
/* Prepare the SMBlockingX message. */
-
prepare_break_message( outbuf, fsp, False);
+
+ /* Save the server smb signing state. */
+ sign_state = srv_oplock_set_signing(False);
+
if (!send_smb(smbd_server_fd(), outbuf))
exit_server("oplock_break_level2: send_smb failed.");
+
+ /* Restore the sign state to what it was. */
+ srv_oplock_set_signing(sign_state);
}
/*
@@ -1215,7 +1247,51 @@ void release_level_2_oplocks_on_change(files_struct *fsp)
}
/****************************************************************************
-setup oplocks for this process
+ Send a 'retry your open' message to a process with a deferred open entry.
+****************************************************************************/
+
+BOOL send_deferred_open_retry_message(deferred_open_entry *entry)
+{
+ char de_msg[DEFERRED_OPEN_MSG_LEN];
+ struct sockaddr_in addr_out;
+ pid_t pid = sys_getpid();
+
+ memset(de_msg, '\0', DEFERRED_OPEN_MSG_LEN);
+ SSVAL(de_msg,DEFERRED_OPEN_CMD_OFFSET,RETRY_DEFERRED_OPEN_CMD);
+ memcpy(de_msg+DEFERRED_OPEN_PID_OFFSET,(char *)&pid,sizeof(pid));
+ memcpy(de_msg+DEFERRED_OPEN_DEV_OFFSET,(char *)&entry->dev,sizeof(entry->dev));
+ memcpy(de_msg+DEFERRED_OPEN_INODE_OFFSET,(char *)&entry->inode,sizeof(entry->inode));
+ memcpy(de_msg+DEFERRED_OPEN_MID_OFFSET,(char *)&entry->mid,sizeof(entry->mid));
+
+ /* Set the address and port. */
+ memset((char *)&addr_out,'\0',sizeof(addr_out));
+ addr_out.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ addr_out.sin_port = htons( entry->port );
+ addr_out.sin_family = AF_INET;
+
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "send_deferred_open_retry_message: sending a message to ");
+ dbgtext( "pid %d on port %d ", (int)entry->pid, entry->port );
+ dbgtext( "for dev = %x, inode = %.0f, mid = %u\n",
+ (unsigned int)entry->dev, (double)entry->inode, (unsigned int)entry->mid );
+ }
+
+ if(sys_sendto(oplock_sock,de_msg,DEFERRED_OPEN_MSG_LEN,0,
+ (struct sockaddr *)&addr_out,sizeof(addr_out)) < 0) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "send_deferred_open_retry_message: failed sending a message to ");
+ dbgtext( "pid %d on port %d ", (int)entry->pid, entry->port );
+ dbgtext( "for dev = %x, inode = %.0f, mid = %u\n",
+ (unsigned int)entry->dev, (double)entry->inode, (unsigned int)entry->mid );
+ dbgtext( "Error was %s\n", strerror(errno) );
+ }
+ return False;
+ }
+ return True;
+}
+
+/****************************************************************************
+ Setup oplocks for this process.
****************************************************************************/
BOOL init_oplocks(void)
diff --git a/source/smbd/posix_acls.c b/source/smbd/posix_acls.c
index 584164e9309..2d9591e6baa 100644
--- a/source/smbd/posix_acls.c
+++ b/source/smbd/posix_acls.c
@@ -880,7 +880,7 @@ static mode_t map_nt_perms( SEC_ACCESS sec_access, int type)
Unpack a SEC_DESC into a UNIX owner and group.
****************************************************************************/
-static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, SEC_DESC *psd)
+static BOOL unpack_nt_owners(int snum, SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, SEC_DESC *psd)
{
DOM_SID owner_sid;
DOM_SID grp_sid;
@@ -910,15 +910,17 @@ static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp,
if (security_info_sent & OWNER_SECURITY_INFORMATION) {
sid_copy(&owner_sid, psd->owner_sid);
if (!NT_STATUS_IS_OK(sid_to_uid(&owner_sid, puser))) {
-#if ACL_FORCE_UNMAPPABLE
- /* this allows take ownership to work reasonably */
- extern struct current_user current_user;
- *puser = current_user.uid;
-#else
- DEBUG(3,("unpack_nt_owners: unable to validate owner sid for %s\n",
- sid_string_static(&owner_sid)));
- return False;
-#endif
+ if (lp_force_unknown_acl_user(snum)) {
+ /* this allows take ownership to work
+ * reasonably */
+ extern struct current_user current_user;
+ *puser = current_user.uid;
+ } else {
+ DEBUG(3,("unpack_nt_owners: unable to validate"
+ " owner sid for %s\n",
+ sid_string_static(&owner_sid)));
+ return False;
+ }
}
}
@@ -930,14 +932,16 @@ static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp,
if (security_info_sent & GROUP_SECURITY_INFORMATION) {
sid_copy(&grp_sid, psd->grp_sid);
if (!NT_STATUS_IS_OK(sid_to_gid( &grp_sid, pgrp))) {
-#if ACL_FORCE_UNMAPPABLE
- /* this allows take group ownership to work reasonably */
- extern struct current_user current_user;
- *pgrp = current_user.gid;
-#else
- DEBUG(3,("unpack_nt_owners: unable to validate group sid.\n"));
- return False;
-#endif
+ if (lp_force_unknown_acl_user(snum)) {
+ /* this allows take group ownership to work
+ * reasonably */
+ extern struct current_user current_user;
+ *pgrp = current_user.gid;
+ } else {
+ DEBUG(3,("unpack_nt_owners: unable to validate"
+ " group sid.\n"));
+ return False;
+ }
}
}
@@ -3005,7 +3009,7 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
* Unpack the user/group/world id's.
*/
- if (!unpack_nt_owners( &sbuf, &user, &grp, security_info_sent, psd))
+ if (!unpack_nt_owners( SNUM(conn), &sbuf, &user, &grp, security_info_sent, psd))
return False;
/*
diff --git a/source/smbd/process.c b/source/smbd/process.c
index 12fd809b784..60ce1499e8d 100644
--- a/source/smbd/process.c
+++ b/source/smbd/process.c
@@ -61,55 +61,215 @@ uint16 get_current_mid(void)
for processing.
****************************************************************************/
-typedef struct {
- ubi_slNode msg_next;
- char *msg_buf;
- int msg_len;
-} pending_message_list;
+static struct pending_message_list *smb_oplock_queue;
+static struct pending_message_list *smb_sharing_violation_queue;
-static ubi_slList smb_oplock_queue = { NULL, (ubi_slNodePtr)&smb_oplock_queue, 0};
+enum q_type { OPLOCK_QUEUE, SHARE_VIOLATION_QUEUE };
+
+/****************************************************************************
+ Free up a message.
+****************************************************************************/
+
+static void free_queued_message(struct pending_message_list *msg)
+{
+ data_blob_free(&msg->buf);
+ data_blob_free(&msg->private_data);
+ SAFE_FREE(msg);
+}
/****************************************************************************
Function to push a message onto the tail of a linked list of smb messages ready
for processing.
****************************************************************************/
-static BOOL push_message(ubi_slList *list_head, char *buf, int msg_len)
+static BOOL push_queued_message(enum q_type qt, char *buf, int msg_len, struct timeval *ptv, char *private, size_t private_len)
{
- pending_message_list *msg = (pending_message_list *)
- malloc(sizeof(pending_message_list));
+ struct pending_message_list *tmp_msg;
+ struct pending_message_list *msg = (struct pending_message_list *)
+ malloc(sizeof(struct pending_message_list));
if(msg == NULL) {
DEBUG(0,("push_message: malloc fail (1)\n"));
return False;
}
- msg->msg_buf = (char *)malloc(msg_len);
- if(msg->msg_buf == NULL) {
+ memset(msg,'\0',sizeof(*msg));
+
+ msg->buf = data_blob(buf, msg_len);
+ if(msg->buf.data == NULL) {
DEBUG(0,("push_message: malloc fail (2)\n"));
SAFE_FREE(msg);
return False;
}
- memcpy(msg->msg_buf, buf, msg_len);
- msg->msg_len = msg_len;
+ if (ptv) {
+ msg->msg_time = *ptv;
+ }
+
+ if (private) {
+ msg->private_data = data_blob(private, private_len);
+ if (msg->private_data.data == NULL) {
+ DEBUG(0,("push_message: malloc fail (3)\n"));
+ data_blob_free(&msg->buf);
+ SAFE_FREE(msg);
+ }
+ }
- ubi_slAddTail( list_head, msg);
+ if (qt == OPLOCK_QUEUE) {
+ DLIST_ADD_END(smb_oplock_queue, msg, tmp_msg);
+ } else {
+ DLIST_ADD_END(smb_sharing_violation_queue, msg, tmp_msg);
+ }
- /* Push the MID of this packet on the signing queue. */
- srv_defer_sign_response(SVAL(buf,smb_mid));
+ DEBUG(10,("push_message: pushed message length %u on queue %s\n",
+ (unsigned int)msg_len,
+ qt == OPLOCK_QUEUE ? "smb_oplock_queue" : "smb_sharing_violation_queue" ));
return True;
}
/****************************************************************************
- Function to push a smb message onto a linked list of local smb messages ready
+ Function to push an oplock smb message onto a linked list of local smb messages ready
for processing.
****************************************************************************/
BOOL push_oplock_pending_smb_message(char *buf, int msg_len)
{
- return push_message(&smb_oplock_queue, buf, msg_len);
+ BOOL ret = push_queued_message(OPLOCK_QUEUE, buf, msg_len, NULL, NULL, 0);
+ if (ret) {
+ /* Push the MID of this packet on the signing queue. */
+ srv_defer_sign_response(SVAL(buf,smb_mid));
+ }
+ return ret;
+}
+
+/****************************************************************************
+ Function to delete a sharing violation open message by mid.
+****************************************************************************/
+
+void remove_sharing_violation_open_smb_message(uint16 mid)
+{
+ struct pending_message_list *pml;
+
+ if (!lp_defer_sharing_violations()) {
+ return;
+ }
+
+ for (pml = smb_sharing_violation_queue; pml; pml = pml->next) {
+ if (mid == SVAL(pml->buf.data,smb_mid)) {
+ DEBUG(10,("remove_sharing_violation_open_smb_message: deleting mid %u len %u\n",
+ (unsigned int)mid, (unsigned int)pml->buf.length ));
+ DLIST_REMOVE(smb_sharing_violation_queue, pml);
+ free_queued_message(pml);
+ return;
+ }
+ }
+}
+
+/****************************************************************************
+ Move a sharing violation open retry message to the front of the list and
+ schedule it for immediate processing.
+****************************************************************************/
+
+void schedule_sharing_violation_open_smb_message(uint16 mid)
+{
+ struct pending_message_list *pml;
+ int i = 0;
+
+ if (!lp_defer_sharing_violations()) {
+ return;
+ }
+
+ for (pml = smb_sharing_violation_queue; pml; pml = pml->next) {
+ uint16 msg_mid = SVAL(pml->buf.data,smb_mid);
+ DEBUG(10,("schedule_sharing_violation_open_smb_message: [%d] msg_mid = %u\n", i++,
+ (unsigned int)msg_mid ));
+ if (mid == msg_mid) {
+ DEBUG(10,("schedule_sharing_violation_open_smb_message: scheduling mid %u\n",
+ mid ));
+ pml->msg_time.tv_sec = 0;
+ pml->msg_time.tv_usec = 0;
+ DLIST_PROMOTE(smb_sharing_violation_queue, pml);
+ return;
+ }
+ }
+
+ DEBUG(10,("schedule_sharing_violation_open_smb_message: failed to find message mid %u\n",
+ mid ));
+}
+
+/****************************************************************************
+ Return true if this mid is on the deferred queue.
+****************************************************************************/
+
+BOOL open_was_deferred(uint16 mid)
+{
+ struct pending_message_list *pml;
+
+ if (!lp_defer_sharing_violations()) {
+ return False;
+ }
+
+ for (pml = smb_sharing_violation_queue; pml; pml = pml->next) {
+ if (SVAL(pml->buf.data,smb_mid) == mid) {
+ return True;
+ }
+ }
+ return False;
+}
+
+/****************************************************************************
+ Return the message queued by this mid.
+****************************************************************************/
+
+struct pending_message_list *get_open_deferred_message(uint16 mid)
+{
+ struct pending_message_list *pml;
+
+ if (!lp_defer_sharing_violations()) {
+ return NULL;
+ }
+
+ for (pml = smb_sharing_violation_queue; pml; pml = pml->next) {
+ if (SVAL(pml->buf.data,smb_mid) == mid) {
+ return pml;
+ }
+ }
+ return NULL;
+}
+
+/****************************************************************************
+ Function to push a sharing violation open smb message onto a linked list of local smb messages ready
+ for processing.
+****************************************************************************/
+
+BOOL push_sharing_violation_open_smb_message(struct timeval *ptv, char *private, size_t priv_len)
+{
+ uint16 mid = SVAL(InBuffer,smb_mid);
+ struct timeval tv;
+ SMB_BIG_INT tdif;
+
+ if (!lp_defer_sharing_violations()) {
+ return True;
+ }
+
+ tv = *ptv;
+ tdif = tv.tv_sec;
+ tdif *= 1000000;
+ tdif += tv.tv_usec;
+
+ /* Add on the timeout. */
+ tdif += SHARING_VIOLATION_USEC_WAIT;
+
+ tv.tv_sec = tdif / 1000000;
+ tv.tv_usec = tdif % 1000000;
+
+ DEBUG(10,("push_sharing_violation_open_smb_message: pushing message len %u mid %u\
+ timeout time [%u.%06u]\n", (unsigned int) smb_len(InBuffer)+4, (unsigned int)mid,
+ (unsigned int)tv.tv_sec, (unsigned int)tv.tv_usec));
+
+ return push_queued_message(SHARE_VIOLATION_QUEUE, InBuffer,
+ smb_len(InBuffer)+4, &tv, private, priv_len);
}
/****************************************************************************
@@ -168,12 +328,17 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout)
fd_set fds;
int selrtn;
struct timeval to;
+ struct timeval *pto;
int maxfd;
smb_read_error = 0;
again:
+ to.tv_sec = timeout / 1000;
+ to.tv_usec = (timeout % 1000) * 1000;
+ pto = timeout > 0 ? &to : NULL;
+
/*
* Note that this call must be before processing any SMB
* messages as we need to synchronously process any messages
@@ -185,18 +350,57 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout)
* Check to see if we already have a message on the smb queue.
* If so - copy and return it.
*/
- if(ubi_slCount(&smb_oplock_queue) != 0) {
- pending_message_list *msg = (pending_message_list *)ubi_slRemHead(&smb_oplock_queue);
- memcpy(buffer, msg->msg_buf, MIN(buffer_len, msg->msg_len));
+ if(smb_oplock_queue != NULL) {
+ struct pending_message_list *msg = smb_oplock_queue;
+ memcpy(buffer, msg->buf.data, MIN(buffer_len, msg->buf.length));
/* Free the message we just copied. */
- SAFE_FREE(msg->msg_buf);
- SAFE_FREE(msg);
+ DLIST_REMOVE(smb_oplock_queue, msg);
+ free_queued_message(msg);
DEBUG(5,("receive_message_or_smb: returning queued smb message.\n"));
return True;
}
+ /*
+ * Check to see if we already have a message on the deferred open queue
+ * and it's time to schedule.
+ */
+ if(smb_sharing_violation_queue != NULL) {
+ BOOL pop_message = False;
+ struct pending_message_list *msg = smb_sharing_violation_queue;
+
+ if (msg->msg_time.tv_sec == 0 && msg->msg_time.tv_usec == 0) {
+ pop_message = True;
+ } else {
+ struct timeval tv;
+ SMB_BIG_INT tdif;
+
+ GetTimeOfDay(&tv);
+ tdif = usec_time_diff(&msg->msg_time, &tv);
+ if (tdif <= 0) {
+ /* Timed out. Schedule...*/
+ pop_message = True;
+ DEBUG(10,("receive_message_or_smb: queued message timed out.\n"));
+ } else {
+ /* Make a more accurate select timeout. */
+ to.tv_sec = tdif / 1000000;
+ to.tv_usec = tdif % 1000000;
+ pto = &to;
+ DEBUG(10,("receive_message_or_smb: select with timeout of [%u.%06u]\n",
+ (unsigned int)pto->tv_sec, (unsigned int)pto->tv_usec ));
+ }
+ }
+
+ if (pop_message) {
+ memcpy(buffer, msg->buf.data, MIN(buffer_len, msg->buf.length));
+
+ /* We leave this message on the queue so the open code can
+ know this is a retry. */
+ DEBUG(5,("receive_message_or_smb: returning deferred open smb message.\n"));
+ return True;
+ }
+ }
/*
* Setup the select read fd set.
@@ -227,10 +431,7 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout)
FD_SET(smbd_server_fd(),&fds);
maxfd = setup_oplock_select_set(&fds);
- to.tv_sec = timeout / 1000;
- to.tv_usec = (timeout % 1000) * 1000;
-
- selrtn = sys_select(MAX(maxfd,smbd_server_fd())+1,&fds,NULL,NULL,timeout>0?&to:NULL);
+ selrtn = sys_select(MAX(maxfd,smbd_server_fd())+1,&fds,NULL,NULL,pto);
/* if we get EINTR then maybe we have received an oplock
signal - treat this as select returning 1. This is ugly, but
@@ -755,7 +956,7 @@ static int switch_message(int type,char *inbuf,char *outbuf,int size,int bufsize
return(ERROR_DOS(ERRSRV,ERRaccess));
/* load service specific parameters */
- if (conn && !set_current_service(conn,(flags & (AS_USER|DO_CHDIR)?True:False)))
+ if (conn && !set_current_service(conn,SVAL(inbuf,smb_flg),(flags & (AS_USER|DO_CHDIR)?True:False)))
return(ERROR_DOS(ERRSRV,ERRaccess));
/* does this protocol need to be run as guest? */
diff --git a/source/smbd/quotas.c b/source/smbd/quotas.c
index e439c1e571a..3c4d4319f63 100644
--- a/source/smbd/quotas.c
+++ b/source/smbd/quotas.c
@@ -933,6 +933,176 @@ BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB
#include <devnm.h>
#endif
+#if defined(__FreeBSD__)
+
+#include <rpc/rpc.h>
+#include <rpc/types.h>
+#include <rpcsvc/rquota.h>
+#include <rpc/nettype.h>
+#include <rpc/xdr.h>
+
+static int quotastat;
+
+static int my_xdr_getquota_args(XDR *xdrsp, struct getquota_args *args)
+{
+ if (!xdr_string(xdrsp, &args->gqa_pathp, RQ_PATHLEN ))
+ return(0);
+ if (!xdr_int(xdrsp, &args->gqa_uid))
+ return(0);
+ return (1);
+}
+
+static int my_xdr_getquota_rslt(XDR *xdrsp, struct getquota_rslt *gqr)
+{
+ if (!xdr_int(xdrsp, &quotastat)) {
+ DEBUG(6,("nfs_quotas: Status bad or zero\n"));
+ return 0;
+ }
+ if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bsize)) {
+ DEBUG(6,("nfs_quotas: Block size bad or zero\n"));
+ return 0;
+ }
+ if (!xdr_bool(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_active)) {
+ DEBUG(6,("nfs_quotas: Active bad or zero\n"));
+ return 0;
+ }
+ if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bhardlimit)) {
+ DEBUG(6,("nfs_quotas: Hardlimit bad or zero\n"));
+ return 0;
+ }
+ if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bsoftlimit)) {
+ DEBUG(6,("nfs_quotas: Softlimit bad or zero\n"));
+ return 0;
+ }
+ if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_curblocks)) {
+ DEBUG(6,("nfs_quotas: Currentblocks bad or zero\n"));
+ return 0;
+ }
+ return (1);
+}
+
+/* Works on FreeBSD, too. :-) */
+static BOOL nfs_quotas(char *nfspath, uid_t euser_id, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ uid_t uid = euser_id;
+ struct dqblk D;
+ char *mnttype = nfspath;
+ CLIENT *clnt;
+ struct getquota_rslt gqr;
+ struct getquota_args args;
+ char *cutstr, *pathname, *host, *testpath;
+ int len;
+ static struct timeval timeout = {2,0};
+ enum clnt_stat clnt_stat;
+ BOOL ret = True;
+
+ *bsize = *dfree = *dsize = (SMB_BIG_UINT)0;
+
+ len=strcspn(mnttype, ":");
+ pathname=strstr(mnttype, ":");
+ cutstr = (char *) malloc(len+1);
+ if (!cutstr)
+ return False;
+
+ memset(cutstr, '\0', len+1);
+ host = strncat(cutstr,mnttype, sizeof(char) * len );
+ DEBUG(5,("nfs_quotas: looking for mount on \"%s\"\n", cutstr));
+ DEBUG(5,("nfs_quotas: of path \"%s\"\n", mnttype));
+ testpath=strchr_m(mnttype, ':');
+ args.gqa_pathp = testpath+1;
+ args.gqa_uid = uid;
+
+ DEBUG(5,("nfs_quotas: Asking for host \"%s\" rpcprog \"%i\" rpcvers \"%i\" network \"%s\"\n", host, RQUOTAPROG, RQUOTAVERS, "udp"));
+
+ if ((clnt = clnt_create(host, RQUOTAPROG, RQUOTAVERS, "udp")) == NULL) {
+ ret = False;
+ goto out;
+ }
+
+ clnt->cl_auth = authunix_create_default();
+ DEBUG(9,("nfs_quotas: auth_success\n"));
+
+ clnt_stat=clnt_call(clnt, RQUOTAPROC_GETQUOTA, (const xdrproc_t) my_xdr_getquota_args, (caddr_t)&args, (const xdrproc_t) my_xdr_getquota_rslt, (caddr_t)&gqr, timeout);
+
+ if (clnt_stat != RPC_SUCCESS) {
+ DEBUG(9,("nfs_quotas: clnt_call fail\n"));
+ ret = False;
+ goto out;
+ }
+
+ /*
+ * quotastat returns 0 if the rpc call fails, 1 if quotas exist, 2 if there is
+ * no quota set, and 3 if no permission to get the quota. If 0 or 3 return
+ * something sensible.
+ */
+
+ switch ( quotastat ) {
+ case 0:
+ DEBUG(9,("nfs_quotas: Remote Quotas Failed! Error \"%i\" \n", quotastat ));
+ ret = False;
+ goto out;
+
+ case 1:
+ DEBUG(9,("nfs_quotas: Good quota data\n"));
+ D.dqb_bsoftlimit = gqr.getquota_rslt_u.gqr_rquota.rq_bsoftlimit;
+ D.dqb_bhardlimit = gqr.getquota_rslt_u.gqr_rquota.rq_bhardlimit;
+ D.dqb_curblocks = gqr.getquota_rslt_u.gqr_rquota.rq_curblocks;
+ break;
+
+ case 2:
+ case 3:
+ D.dqb_bsoftlimit = 1;
+ D.dqb_curblocks = 1;
+ DEBUG(9,("nfs_quotas: Remote Quotas returned \"%i\" \n", quotastat ));
+ break;
+
+ default:
+ DEBUG(9,("nfs_quotas: Remote Quotas Questionable! Error \"%i\" \n", quotastat ));
+ break;
+ }
+
+ DEBUG(10,("nfs_quotas: Let`s look at D a bit closer... status \"%i\" bsize \"%i\" active? \"%i\" bhard \"%i\" bsoft \"%i\" curb \"%i\" \n",
+ quotastat,
+ gqr.getquota_rslt_u.gqr_rquota.rq_bsize,
+ gqr.getquota_rslt_u.gqr_rquota.rq_active,
+ gqr.getquota_rslt_u.gqr_rquota.rq_bhardlimit,
+ gqr.getquota_rslt_u.gqr_rquota.rq_bsoftlimit,
+ gqr.getquota_rslt_u.gqr_rquota.rq_curblocks));
+
+ if (D.dqb_bsoftlimit == 0)
+ D.dqb_bsoftlimit = D.dqb_bhardlimit;
+ if (D.dqb_bsoftlimit == 0)
+ return False;
+
+ *bsize = gqr.getquota_rslt_u.gqr_rquota.rq_bsize;
+ *dsize = D.dqb_bsoftlimit;
+
+ if (D.dqb_curblocks == D.dqb_curblocks == 1)
+ *bsize = DEV_BSIZE;
+
+ if (D.dqb_curblocks > D.dqb_bsoftlimit) {
+ *dfree = 0;
+ *dsize = D.dqb_curblocks;
+ } else
+ *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
+
+ out:
+
+ if (clnt) {
+ if (clnt->cl_auth)
+ auth_destroy(clnt->cl_auth);
+ clnt_destroy(clnt);
+ }
+
+ DEBUG(5,("nfs_quotas: For path \"%s\" returning bsize %.0f, dfree %.0f, dsize %.0f\n",args.gqa_pathp,(double)*bsize,(double)*dfree,(double)*dsize));
+
+ SAFE_FREE(cutstr);
+ DEBUG(10,("nfs_quotas: End of nfs_quotas\n" ));
+ return ret;
+}
+
+#endif
+
/****************************************************************************
try to get the disk space from disk quotas - default version
****************************************************************************/
@@ -976,10 +1146,42 @@ BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB
{
/* FreeBSD patches from Marty Moll <martym@arbor.edu> */
gid_t egrp_id;
-
+#if defined(__FreeBSD__)
+ SMB_DEV_T devno;
+ struct statfs *mnts;
+ SMB_STRUCT_STAT st;
+ int mntsize, i;
+
+ if (sys_stat(path,&st) < 0)
+ return False;
+ devno = st.st_dev;
+
+ mntsize = getmntinfo(&mnts,MNT_NOWAIT);
+ if (mntsize <= 0)
+ return False;
+
+ for (i = 0; i < mntsize; i++) {
+ if (sys_stat(mnts[i].f_mntonname,&st) < 0)
+ return False;
+ if (st.st_dev == devno)
+ break;
+ }
+ if (i == mntsize)
+ return False;
+#endif
+
save_re_uid();
set_effective_uid(0);
+#if defined(__FreeBSD__)
+ if (strcmp(mnts[i].f_fstypename,"nfs") == 0) {
+ BOOL retval;
+ retval = nfs_quotas(mnts[i].f_mntfromname,euser_id,bsize,dfree,dsize);
+ restore_re_uid();
+ return retval;
+ }
+#endif
+
egrp_id = getegid();
r= quotactl(path,QCMD(Q_GETQUOTA,USRQUOTA),euser_id,(char *) &D);
diff --git a/source/smbd/reply.c b/source/smbd/reply.c
index 560208ae157..71efb793af0 100644
--- a/source/smbd/reply.c
+++ b/source/smbd/reply.c
@@ -43,7 +43,7 @@ extern BOOL global_encrypted_passwords_negotiated;
set.
****************************************************************************/
-NTSTATUS check_path_syntax(pstring destname, const pstring srcname)
+NTSTATUS check_path_syntax(pstring destname, const pstring srcname, BOOL allow_wcard_names)
{
char *d = destname;
const char *s = srcname;
@@ -119,7 +119,21 @@ NTSTATUS check_path_syntax(pstring destname, const pstring srcname)
s++;
} else {
if (!(*s & 0x80)) {
- *d++ = *s++;
+ if (allow_wcard_names) {
+ *d++ = *s++;
+ } else {
+ switch (*s) {
+ case '*':
+ case '?':
+ case '<':
+ case '>':
+ case '"':
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ default:
+ *d++ = *s++;
+ break;
+ }
+ }
} else {
switch(next_mb_char_size(s)) {
case 4:
@@ -147,7 +161,7 @@ NTSTATUS check_path_syntax(pstring destname, const pstring srcname)
Pull a string and check the path - provide for error return.
****************************************************************************/
-size_t srvstr_get_path(char *inbuf, char *dest, const char *src, size_t dest_len, size_t src_len, int flags, NTSTATUS *err)
+size_t srvstr_get_path(char *inbuf, char *dest, const char *src, size_t dest_len, size_t src_len, int flags, NTSTATUS *err, BOOL allow_wcard_names)
{
pstring tmppath;
char *tmppath_ptr = tmppath;
@@ -161,7 +175,7 @@ size_t srvstr_get_path(char *inbuf, char *dest, const char *src, size_t dest_len
} else {
ret = srvstr_pull( inbuf, tmppath_ptr, src, dest_len, src_len, flags);
}
- *err = check_path_syntax(dest, tmppath);
+ *err = check_path_syntax(dest, tmppath, allow_wcard_names);
return ret;
}
@@ -516,7 +530,7 @@ int reply_chkpth(connection_struct *conn, char *inbuf,char *outbuf, int dum_size
START_PROFILE(SMBchkpth);
- srvstr_get_path(inbuf, name, smb_buf(inbuf) + 1, sizeof(name), 0, STR_TERMINATE, &status);
+ srvstr_get_path(inbuf, name, smb_buf(inbuf) + 1, sizeof(name), 0, STR_TERMINATE, &status, False);
if (!NT_STATUS_IS_OK(status)) {
END_PROFILE(SMBchkpth);
return ERROR_NT(status);
@@ -525,6 +539,10 @@ int reply_chkpth(connection_struct *conn, char *inbuf,char *outbuf, int dum_size
RESOLVE_DFSPATH(name, conn, inbuf, outbuf);
unix_convert(name,conn,0,&bad_path,&sbuf);
+ if (bad_path) {
+ END_PROFILE(SMBchkpth);
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ }
mode = SVAL(inbuf,smb_vwv0);
@@ -548,18 +566,11 @@ int reply_chkpth(connection_struct *conn, char *inbuf,char *outbuf, int dum_size
* the parent directory is valid but not the
* last component - it returns NT_STATUS_OBJECT_NAME_NOT_FOUND
* for that case and NT_STATUS_OBJECT_PATH_NOT_FOUND
- * if the path is invalid.
+ * if the path is invalid. This is different from set_bad_path_error()
+ * in the non-NT error case.
*/
- if (bad_path) {
- END_PROFILE(SMBchkpth);
- return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
- } else {
- END_PROFILE(SMBchkpth);
- return ERROR_NT(NT_STATUS_OBJECT_NAME_NOT_FOUND);
- }
- } else if (errno == ENOTDIR) {
END_PROFILE(SMBchkpth);
- return ERROR_NT(NT_STATUS_NOT_A_DIRECTORY);
+ return ERROR_BOTH(NT_STATUS_OBJECT_NAME_NOT_FOUND,ERRDOS,ERRbadpath);
}
END_PROFILE(SMBchkpth);
@@ -594,7 +605,7 @@ int reply_getatr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size
START_PROFILE(SMBgetatr);
p = smb_buf(inbuf) + 1;
- p += srvstr_get_path(inbuf, fname, p, sizeof(fname), 0, STR_TERMINATE, &status);
+ p += srvstr_get_path(inbuf, fname, p, sizeof(fname), 0, STR_TERMINATE, &status, False);
if (!NT_STATUS_IS_OK(status)) {
END_PROFILE(SMBgetatr);
return ERROR_NT(status);
@@ -613,6 +624,10 @@ int reply_getatr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size
ok = True;
} else {
unix_convert(fname,conn,0,&bad_path,&sbuf);
+ if (bad_path) {
+ END_PROFILE(SMBgetatr);
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ }
if (check_name(fname,conn)) {
if (VALID_STAT(sbuf) || SMB_VFS_STAT(conn,fname,&sbuf) == 0) {
mode = dos_mode(conn,fname,&sbuf);
@@ -669,13 +684,17 @@ int reply_setatr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size
START_PROFILE(SMBsetatr);
p = smb_buf(inbuf) + 1;
- p += srvstr_get_path(inbuf, fname, p, sizeof(fname), 0, STR_TERMINATE, &status);
+ p += srvstr_get_path(inbuf, fname, p, sizeof(fname), 0, STR_TERMINATE, &status, False);
if (!NT_STATUS_IS_OK(status)) {
END_PROFILE(SMBsetatr);
return ERROR_NT(status);
}
unix_convert(fname,conn,0,&bad_path,&sbuf);
+ if (bad_path) {
+ END_PROFILE(SMBsetatr);
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ }
mode = SVAL(inbuf,smb_vwv0);
mtime = make_unix_date3(inbuf+smb_vwv1);
@@ -798,7 +817,7 @@ int reply_search(connection_struct *conn, char *inbuf,char *outbuf, int dum_size
maxentries = SVAL(inbuf,smb_vwv0);
dirtype = SVAL(inbuf,smb_vwv1);
p = smb_buf(inbuf) + 1;
- p += srvstr_get_path(inbuf, path, p, sizeof(path), 0, STR_TERMINATE, &nt_status);
+ p += srvstr_get_path(inbuf, path, p, sizeof(path), 0, STR_TERMINATE, &nt_status, True);
if (!NT_STATUS_IS_OK(nt_status)) {
END_PROFILE(SMBsearch);
return ERROR_NT(nt_status);
@@ -976,7 +995,7 @@ int reply_fclose(connection_struct *conn, char *inbuf,char *outbuf, int dum_size
outsize = set_message(outbuf,1,0,True);
p = smb_buf(inbuf) + 1;
- p += srvstr_get_path(inbuf, path, p, sizeof(path), 0, STR_TERMINATE, &err);
+ p += srvstr_get_path(inbuf, path, p, sizeof(path), 0, STR_TERMINATE, &err, True);
if (!NT_STATUS_IS_OK(err)) {
END_PROFILE(SMBfclose);
return ERROR_NT(err);
@@ -1028,7 +1047,7 @@ int reply_open(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
share_mode = SVAL(inbuf,smb_vwv0);
- srvstr_get_path(inbuf, fname, smb_buf(inbuf)+1, sizeof(fname), 0, STR_TERMINATE, &status);
+ srvstr_get_path(inbuf, fname, smb_buf(inbuf)+1, sizeof(fname), 0, STR_TERMINATE, &status, False);
if (!NT_STATUS_IS_OK(status)) {
END_PROFILE(SMBopen);
return ERROR_NT(status);
@@ -1037,12 +1056,21 @@ int reply_open(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
unix_convert(fname,conn,0,&bad_path,&sbuf);
+ if (bad_path) {
+ END_PROFILE(SMBopen);
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ }
fsp = open_file_shared(conn,fname,&sbuf,share_mode,(FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
(uint32)dos_attr, oplock_request,&rmode,NULL);
if (!fsp) {
END_PROFILE(SMBopen);
+ if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+ /* We have re-scheduled this call. */
+ clear_cached_errors();
+ return -1;
+ }
return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess);
}
@@ -1117,7 +1145,7 @@ int reply_open_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt
}
/* XXXX we need to handle passed times, sattr and flags */
- srvstr_get_path(inbuf, fname, smb_buf(inbuf), sizeof(fname), 0, STR_TERMINATE, &status);
+ srvstr_get_path(inbuf, fname, smb_buf(inbuf), sizeof(fname), 0, STR_TERMINATE, &status, False);
if (!NT_STATUS_IS_OK(status)) {
END_PROFILE(SMBopenX);
return ERROR_NT(status);
@@ -1126,12 +1154,21 @@ int reply_open_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt
RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
unix_convert(fname,conn,0,&bad_path,&sbuf);
+ if (bad_path) {
+ END_PROFILE(SMBopenX);
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ }
fsp = open_file_shared(conn,fname,&sbuf,smb_mode,smb_ofun,(uint32)smb_attr,
oplock_request, &rmode,&smb_action);
if (!fsp) {
END_PROFILE(SMBopenX);
+ if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+ /* We have re-scheduled this call. */
+ clear_cached_errors();
+ return -1;
+ }
return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess);
}
@@ -1230,7 +1267,7 @@ int reply_mknew(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
com = SVAL(inbuf,smb_com);
createmode = SVAL(inbuf,smb_vwv0);
- srvstr_get_path(inbuf, fname, smb_buf(inbuf) + 1, sizeof(fname), 0, STR_TERMINATE, &status);
+ srvstr_get_path(inbuf, fname, smb_buf(inbuf) + 1, sizeof(fname), 0, STR_TERMINATE, &status, False);
if (!NT_STATUS_IS_OK(status)) {
END_PROFILE(SMBcreate);
return ERROR_NT(status);
@@ -1239,6 +1276,10 @@ int reply_mknew(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
unix_convert(fname,conn,0,&bad_path,&sbuf);
+ if (bad_path) {
+ END_PROFILE(SMBcreate);
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ }
if (createmode & aVOLID)
DEBUG(0,("Attempt to create file (%s) with volid set - please report this\n",fname));
@@ -1257,6 +1298,11 @@ int reply_mknew(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
if (!fsp) {
END_PROFILE(SMBcreate);
+ if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+ /* We have re-scheduled this call. */
+ clear_cached_errors();
+ return -1;
+ }
return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess);
}
@@ -1297,7 +1343,7 @@ int reply_ctemp(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
START_PROFILE(SMBctemp);
createattr = SVAL(inbuf,smb_vwv0);
- srvstr_get_path(inbuf, fname, smb_buf(inbuf)+1, sizeof(fname), 0, STR_TERMINATE, &status);
+ srvstr_get_path(inbuf, fname, smb_buf(inbuf)+1, sizeof(fname), 0, STR_TERMINATE, &status, False);
if (!NT_STATUS_IS_OK(status)) {
END_PROFILE(SMBctemp);
return ERROR_NT(status);
@@ -1311,6 +1357,10 @@ int reply_ctemp(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
unix_convert(fname,conn,0,&bad_path,&sbuf);
+ if (bad_path) {
+ END_PROFILE(SMBctemp);
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ }
tmpfd = smb_mkstemp(fname);
if (tmpfd == -1) {
@@ -1332,6 +1382,11 @@ int reply_ctemp(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
if (!fsp) {
END_PROFILE(SMBctemp);
+ if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+ /* We have re-scheduled this call. */
+ clear_cached_errors();
+ return -1;
+ }
return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess);
}
@@ -1373,15 +1428,20 @@ int reply_ctemp(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
Check if a user is allowed to rename a file.
********************************************************************/
-static NTSTATUS can_rename(char *fname,connection_struct *conn, SMB_STRUCT_STAT *pst)
+static NTSTATUS can_rename(char *fname,connection_struct *conn, uint16 dirtype, SMB_STRUCT_STAT *pst)
{
int smb_action;
int access_mode;
files_struct *fsp;
+ uint16 fmode;
if (!CAN_WRITE(conn))
return NT_STATUS_MEDIA_WRITE_PROTECTED;
-
+
+ fmode = dos_mode(conn,fname,pst);
+ if ((fmode & ~dirtype) & (aHIDDEN | aSYSTEM))
+ return NT_STATUS_NO_SUCH_FILE;
+
if (S_ISDIR(pst->st_mode))
return NT_STATUS_OK;
@@ -1561,7 +1621,7 @@ NTSTATUS unlink_internals(connection_struct *conn, int dirtype, char *name)
/* Quick check for "." and ".." */
if (fname[0] == '.') {
if (!fname[1] || (fname[1] == '.' && !fname[2])) {
- if ((dirtype & aDIR)) {
+ if ((dirtype & FILE_ATTRIBUTE_DIRECTORY) && (dirtype & FILE_ATTRIBUTE_SYSTEM)) {
sys_direntry = True;
} else {
continue;
@@ -1574,6 +1634,8 @@ NTSTATUS unlink_internals(connection_struct *conn, int dirtype, char *name)
if (sys_direntry) {
error = NT_STATUS_OBJECT_NAME_INVALID;
+ DEBUG(3,("unlink_internals: system directory delete denied [%s] mask [%s]\n",
+ fname, mask));
break;
}
@@ -1612,7 +1674,7 @@ int reply_unlink(connection_struct *conn, char *inbuf,char *outbuf, int dum_size
dirtype = SVAL(inbuf,smb_vwv0);
- srvstr_get_path(inbuf, name, smb_buf(inbuf) + 1, sizeof(name), 0, STR_TERMINATE, &status);
+ srvstr_get_path(inbuf, name, smb_buf(inbuf) + 1, sizeof(name), 0, STR_TERMINATE, &status, True);
if (!NT_STATUS_IS_OK(status)) {
END_PROFILE(SMBunlink);
return ERROR_NT(status);
@@ -1623,8 +1685,14 @@ int reply_unlink(connection_struct *conn, char *inbuf,char *outbuf, int dum_size
DEBUG(3,("reply_unlink : %s\n",name));
status = unlink_internals(conn, dirtype, name);
- if (!NT_STATUS_IS_OK(status))
+ if (!NT_STATUS_IS_OK(status)) {
+ if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+ /* We have re-scheduled this call. */
+ clear_cached_errors();
+ return -1;
+ }
return ERROR_NT(status);
+ }
/*
* Win2k needs a changenotify request response before it will
@@ -1661,12 +1729,13 @@ void send_file_readbraw(connection_struct *conn, files_struct *fsp, SMB_OFF_T st
#if defined(WITH_SENDFILE)
/*
- * We can only use sendfile on a non-chained packet and on a file
- * that is exclusively oplocked. reply_readbraw has already checked the length.
+ * We can only use sendfile on a non-chained packet
+ * but we can use on a non-oplocked file. tridge proved this
+ * on a train in Germany :-). JRA.
+ * reply_readbraw has already checked the length.
*/
- if ((nread > 0) && (lp_write_cache_size(SNUM(conn)) == 0) &&
- EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && lp_use_sendfile(SNUM(conn)) ) {
+ if ((nread > 0) && (lp_write_cache_size(SNUM(conn)) == 0) && lp_use_sendfile(SNUM(conn)) ) {
DATA_BLOB header;
_smb_setlen(outbuf,nread);
@@ -1999,12 +2068,13 @@ int send_file_readX(connection_struct *conn, char *inbuf,char *outbuf,int length
#if defined(WITH_SENDFILE)
/*
- * We can only use sendfile on a non-chained packet and on a file
- * that is exclusively oplocked.
+ * We can only use sendfile on a non-chained packet
+ * but we can use on a non-oplocked file. tridge proved this
+ * on a train in Germany :-). JRA.
*/
- if ((CVAL(inbuf,smb_vwv0) == 0xFF) && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) &&
- lp_use_sendfile(SNUM(conn)) && (lp_write_cache_size(SNUM(conn)) == 0) ) {
+ if ((CVAL(inbuf,smb_vwv0) == 0xFF) && lp_use_sendfile(SNUM(conn)) &&
+ (lp_write_cache_size(SNUM(conn)) == 0) ) {
SMB_STRUCT_STAT sbuf;
DATA_BLOB header;
@@ -3138,15 +3208,16 @@ NTSTATUS mkdir_internal(connection_struct *conn, pstring directory)
return NT_STATUS_OBJECT_NAME_INVALID;
}
+ if (bad_path) {
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+
if (check_name(directory, conn))
ret = vfs_MkDir(conn,directory,unix_mode(conn,aDIR,directory));
if (ret == -1) {
if(errno == ENOENT) {
- if (bad_path)
- return NT_STATUS_OBJECT_PATH_NOT_FOUND;
- else
- return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
return map_nt_error_from_unix(errno);
}
@@ -3165,7 +3236,7 @@ int reply_mkdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
NTSTATUS status;
START_PROFILE(SMBmkdir);
- srvstr_get_path(inbuf, directory, smb_buf(inbuf) + 1, sizeof(directory), 0, STR_TERMINATE, &status);
+ srvstr_get_path(inbuf, directory, smb_buf(inbuf) + 1, sizeof(directory), 0, STR_TERMINATE, &status, False);
if (!NT_STATUS_IS_OK(status)) {
END_PROFILE(SMBmkdir);
return ERROR_NT(status);
@@ -3335,7 +3406,7 @@ int reply_rmdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
NTSTATUS status;
START_PROFILE(SMBrmdir);
- srvstr_get_path(inbuf, directory, smb_buf(inbuf) + 1, sizeof(directory), 0, STR_TERMINATE, &status);
+ srvstr_get_path(inbuf, directory, smb_buf(inbuf) + 1, sizeof(directory), 0, STR_TERMINATE, &status, False);
if (!NT_STATUS_IS_OK(status)) {
END_PROFILE(SMBrmdir);
return ERROR_NT(status);
@@ -3344,6 +3415,10 @@ int reply_rmdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
RESOLVE_DFSPATH(directory, conn, inbuf, outbuf)
unix_convert(directory,conn, NULL,&bad_path,&sbuf);
+ if (bad_path) {
+ END_PROFILE(SMBrmdir);
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ }
if (check_name(directory,conn)) {
dptr_closepath(directory,SVAL(inbuf,smb_pid));
@@ -3475,7 +3550,7 @@ static void rename_open_files(connection_struct *conn, SMB_DEV_T dev, SMB_INO_T
Rename an open file - given an fsp.
****************************************************************************/
-NTSTATUS rename_internals_fsp(connection_struct *conn, files_struct *fsp, char *newname, BOOL replace_if_exists)
+NTSTATUS rename_internals_fsp(connection_struct *conn, files_struct *fsp, char *newname, uint16 attrs, BOOL replace_if_exists)
{
SMB_STRUCT_STAT sbuf;
BOOL bad_path = False;
@@ -3556,7 +3631,7 @@ NTSTATUS rename_internals_fsp(connection_struct *conn, files_struct *fsp, char *
return NT_STATUS_OBJECT_NAME_COLLISION;
}
- error = can_rename(newname,conn,&sbuf);
+ error = can_rename(newname,conn,attrs,&sbuf);
if (dest_exists && !NT_STATUS_IS_OK(error)) {
DEBUG(3,("rename_internals: Error %s rename %s -> %s\n",
@@ -3762,7 +3837,7 @@ directory = %s, newname = %s, last_component_dest = %s, is_8_3 = %d\n",
return NT_STATUS_OBJECT_PATH_NOT_FOUND;
}
- error = can_rename(directory,conn,&sbuf1);
+ error = can_rename(directory,conn,attrs,&sbuf1);
if (!NT_STATUS_IS_OK(error)) {
DEBUG(3,("rename_internals: Error %s rename %s -> %s\n",
@@ -3853,7 +3928,7 @@ directory = %s, newname = %s, last_component_dest = %s, is_8_3 = %d\n",
DEBUG(6,("rename %s failed. Error %s\n", fname, nt_errstr(error)));
continue;
}
- error = can_rename(fname,conn,&sbuf1);
+ error = can_rename(fname,conn,attrs,&sbuf1);
if (!NT_STATUS_IS_OK(error)) {
DEBUG(6,("rename %s refused\n", fname));
continue;
@@ -3924,13 +3999,13 @@ int reply_mv(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
START_PROFILE(SMBmv);
p = smb_buf(inbuf) + 1;
- p += srvstr_get_path(inbuf, name, p, sizeof(name), 0, STR_TERMINATE, &status);
+ p += srvstr_get_path(inbuf, name, p, sizeof(name), 0, STR_TERMINATE, &status, True);
if (!NT_STATUS_IS_OK(status)) {
END_PROFILE(SMBmv);
return ERROR_NT(status);
}
p++;
- p += srvstr_get_path(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status);
+ p += srvstr_get_path(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status, True);
if (!NT_STATUS_IS_OK(status)) {
END_PROFILE(SMBmv);
return ERROR_NT(status);
@@ -3944,6 +4019,11 @@ int reply_mv(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
status = rename_internals(conn, name, newname, attrs, False);
if (!NT_STATUS_IS_OK(status)) {
END_PROFILE(SMBmv);
+ if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+ /* We have re-scheduled this call. */
+ clear_cached_errors();
+ return -1;
+ }
return ERROR_NT(status);
}
@@ -3989,7 +4069,8 @@ static BOOL copy_file(char *src,char *dest1,connection_struct *conn, int ofun,
return(False);
fsp1 = open_file_shared(conn,src,&src_sbuf,SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_RDONLY),
- (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),FILE_ATTRIBUTE_NORMAL,0,&Access,&action);
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),FILE_ATTRIBUTE_NORMAL,INTERNAL_OPEN_ONLY,
+ &Access,&action);
if (!fsp1)
return(False);
@@ -4002,7 +4083,7 @@ static BOOL copy_file(char *src,char *dest1,connection_struct *conn, int ofun,
ZERO_STRUCTP(&sbuf2);
fsp2 = open_file_shared(conn,dest,&sbuf2,SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_WRONLY),
- ofun,dosattrs,0,&Access,&action);
+ ofun,dosattrs,INTERNAL_OPEN_ONLY,&Access,&action);
if (!fsp2) {
close_file(fsp1,False);
@@ -4070,12 +4151,12 @@ int reply_copy(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
*directory = *mask = 0;
p = smb_buf(inbuf);
- p += srvstr_get_path(inbuf, name, p, sizeof(name), 0, STR_TERMINATE, &status);
+ p += srvstr_get_path(inbuf, name, p, sizeof(name), 0, STR_TERMINATE, &status, True);
if (!NT_STATUS_IS_OK(status)) {
END_PROFILE(SMBcopy);
return ERROR_NT(status);
}
- p += srvstr_get_path(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status);
+ p += srvstr_get_path(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status, True);
if (!NT_STATUS_IS_OK(status)) {
END_PROFILE(SMBcopy);
return ERROR_NT(status);
@@ -4235,7 +4316,7 @@ int reply_setdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size
return ERROR_DOS(ERRDOS,ERRnoaccess);
}
- srvstr_get_path(inbuf, newdir, smb_buf(inbuf) + 1, sizeof(newdir), 0, STR_TERMINATE, &status);
+ srvstr_get_path(inbuf, newdir, smb_buf(inbuf) + 1, sizeof(newdir), 0, STR_TERMINATE, &status, False);
if (!NT_STATUS_IS_OK(status)) {
END_PROFILE(pathworks_setdir);
return ERROR_NT(status);
diff --git a/source/smbd/server.c b/source/smbd/server.c
index 343a835be8a..c3e0da542e2 100644
--- a/source/smbd/server.c
+++ b/source/smbd/server.c
@@ -494,18 +494,16 @@ BOOL reload_services(BOOL test)
load_interfaces();
- {
- if (smbd_server_fd() != -1) {
- set_socket_options(smbd_server_fd(),"SO_KEEPALIVE");
- set_socket_options(smbd_server_fd(), user_socket_options);
- }
+ if (smbd_server_fd() != -1) {
+ set_socket_options(smbd_server_fd(),"SO_KEEPALIVE");
+ set_socket_options(smbd_server_fd(), user_socket_options);
}
mangle_reset_cache();
reset_stat_cache();
/* this forces service parameters to be flushed */
- set_current_service(NULL,True);
+ set_current_service(NULL,0,True);
return(ret);
}
diff --git a/source/smbd/service.c b/source/smbd/service.c
index c74537c299e..3b499d5cc1d 100644
--- a/source/smbd/service.c
+++ b/source/smbd/service.c
@@ -28,10 +28,11 @@ extern userdom_struct current_user_info;
Load parameters specific to a connection/service.
****************************************************************************/
-BOOL set_current_service(connection_struct *conn,BOOL do_chdir)
+BOOL set_current_service(connection_struct *conn, uint16 flags, BOOL do_chdir)
{
extern char magic_char;
static connection_struct *last_conn;
+ static uint16 last_flags;
int snum;
if (!conn) {
@@ -51,10 +52,24 @@ BOOL set_current_service(connection_struct *conn,BOOL do_chdir)
return(False);
}
- if (conn == last_conn)
+ if ((conn == last_conn) && (last_flags == flags)) {
return(True);
+ }
last_conn = conn;
+ last_flags = flags;
+
+ /* Obey the client case sensitivity requests - only for clients that support it. */
+ if (lp_casesensitive(snum) == Auto) {
+ /* We need this uglyness due to DOS/Win9x clients that lie about case insensitivity. */
+ enum remote_arch_types ra_type = get_remote_arch();
+ if ((ra_type != RA_SAMBA) && (ra_type != RA_CIFSFS)) {
+ /* Client can't support per-packet case sensitive pathnames. */
+ conn->case_sensitive = False;
+ } else {
+ conn->case_sensitive = !(flags & FLAG_CASELESS_PATHNAMES);
+ }
+ }
magic_char = lp_magicchar(snum);
return(True);
@@ -347,7 +362,13 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser,
conn->dirptr = NULL;
/* Case options for the share. */
- conn->case_sensitive = lp_casesensitive(snum);
+ if (lp_casesensitive(snum) == Auto) {
+ /* We will be setting this per packet. Set to be case insensitive for now. */
+ conn->case_sensitive = False;
+ } else {
+ conn->case_sensitive = (BOOL)lp_casesensitive(snum);
+ }
+
conn->case_preserve = lp_preservecase(snum);
conn->short_case_preserve = lp_shortpreservecase(snum);
@@ -499,6 +520,20 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser,
return NULL;
}
+ /*
+ * If widelinks are disallowed we need to canonicalise the
+ * connect path here to ensure we don't have any symlinks in
+ * the connectpath. We will be checking all paths on this
+ * connection are below this directory. We must do this after
+ * the VFS init as we depend on the realpath() pointer in the vfs table. JRA.
+ */
+ if (!lp_widelinks(snum)) {
+ pstring s;
+ pstrcpy(s,conn->connectpath);
+ canonicalize_path(conn, s);
+ string_set(&conn->connectpath,s);
+ }
+
/* ROOT Activities: */
/* check number of connections */
if (!claim_connection(conn,
diff --git a/source/smbd/session.c b/source/smbd/session.c
index 61118f13dd9..91ebaeb830b 100644
--- a/source/smbd/session.c
+++ b/source/smbd/session.c
@@ -34,8 +34,8 @@ BOOL session_init(void)
if (tdb)
return True;
- tdb = tdb_open_ex(lock_path("sessionid.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT,
- O_RDWR | O_CREAT, 0644, smbd_tdb_log);
+ tdb = tdb_open_log(lock_path("sessionid.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT,
+ O_RDWR | O_CREAT, 0644);
if (!tdb) {
DEBUG(1,("session_init: failed to open sessionid tdb\n"));
return False;
diff --git a/source/smbd/sesssetup.c b/source/smbd/sesssetup.c
index 8a56478929f..0122b662ebf 100644
--- a/source/smbd/sesssetup.c
+++ b/source/smbd/sesssetup.c
@@ -152,7 +152,6 @@ static int reply_spnego_kerberos(connection_struct *conn,
auth_serversupplied_info *server_info = NULL;
DATA_BLOB session_key = data_blob(NULL, 0);
uint8 tok_id[2];
- BOOL foreign = False;
DATA_BLOB nullblob = data_blob(NULL, 0);
fstring real_username;
@@ -197,7 +196,6 @@ static int reply_spnego_kerberos(connection_struct *conn,
SAFE_FREE(client);
return ERROR_NT(NT_STATUS_LOGON_FAILURE);
}
- foreign = True;
}
/* this gives a fully qualified user name (ie. with full realm).
@@ -244,6 +242,8 @@ static int reply_spnego_kerberos(connection_struct *conn,
/* lookup the passwd struct, create a new user if necessary */
+ map_username( user );
+
pw = smb_getpwnam( user, real_username, True );
if (!pw) {
diff --git a/source/smbd/trans2.c b/source/smbd/trans2.c
index 738d12e020f..a7db9daf7d6 100644
--- a/source/smbd/trans2.c
+++ b/source/smbd/trans2.c
@@ -607,7 +607,7 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, i
if (IS_IPC(conn))
return(ERROR_DOS(ERRSRV,ERRaccess));
- srvstr_get_path(inbuf, fname, pname, sizeof(fname), -1, STR_TERMINATE, &status);
+ srvstr_get_path(inbuf, fname, pname, sizeof(fname), -1, STR_TERMINATE, &status, False);
if (!NT_STATUS_IS_OK(status)) {
return ERROR_NT(status);
}
@@ -618,6 +618,9 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, i
/* XXXX we need to handle passed times, sattr and flags */
unix_convert(fname,conn,0,&bad_path,&sbuf);
+ if (bad_path) {
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ }
if (!check_name(fname,conn)) {
return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
@@ -627,6 +630,11 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, i
oplock_request, &rmode,&smb_action);
if (!fsp) {
+ if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+ /* We have re-scheduled this call. */
+ clear_cached_errors();
+ return -1;
+ }
return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
}
@@ -1371,7 +1379,7 @@ close_if_end = %d requires_resume_key = %d level = 0x%x, max_data_bytes = %d\n",
return(ERROR_DOS(ERRDOS,ERRunknownlevel));
}
- srvstr_get_path(inbuf, directory, params+12, sizeof(directory), -1, STR_TERMINATE, &ntstatus);
+ srvstr_get_path(inbuf, directory, params+12, sizeof(directory), -1, STR_TERMINATE, &ntstatus, True);
if (!NT_STATUS_IS_OK(ntstatus)) {
return ERROR_NT(ntstatus);
}
@@ -1379,6 +1387,9 @@ close_if_end = %d requires_resume_key = %d level = 0x%x, max_data_bytes = %d\n",
RESOLVE_FINDFIRST_DFSPATH(directory, conn, inbuf, outbuf);
unix_convert(directory,conn,0,&bad_path,&sbuf);
+ if (bad_path) {
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ }
if(!check_name(directory,conn)) {
return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
}
@@ -1564,7 +1575,7 @@ static int call_trans2findnext(connection_struct *conn, char *inbuf, char *outbu
*mask = *directory = *resume_name = 0;
- srvstr_get_path(inbuf, resume_name, params+12, sizeof(resume_name), -1, STR_TERMINATE, &ntstatus);
+ srvstr_get_path(inbuf, resume_name, params+12, sizeof(resume_name), -1, STR_TERMINATE, &ntstatus, True);
if (!NT_STATUS_IS_OK(ntstatus)) {
return ERROR_NT(ntstatus);
}
@@ -2257,6 +2268,8 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
if (!params)
return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+ ZERO_STRUCT(sbuf);
+
if (tran_call == TRANSACT2_QFILEINFO) {
if (total_params < 4)
return(ERROR_DOS(ERRDOS,ERRinvalidparam));
@@ -2272,11 +2285,7 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
*/
pstrcpy(fname, fsp->fsp_name);
- unix_convert(fname,conn,0,&bad_path,&sbuf);
- if (!check_name(fname,conn)) {
- DEBUG(3,("call_trans2qfilepathinfo: fileinfo of %s failed for fake_file(%s)\n",fname,strerror(errno)));
- return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
- }
+ /* We know this name is ok, it's already passed the checks. */
} else if(fsp && (fsp->is_directory || fsp->fd == -1)) {
/*
@@ -2284,12 +2293,8 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
* handle (returned from an NT SMB). NT5.0 seems
* to do this call. JRA.
*/
+ /* We know this name is ok, it's already passed the checks. */
pstrcpy(fname, fsp->fsp_name);
- unix_convert(fname,conn,0,&bad_path,&sbuf);
- if (!check_name(fname,conn)) {
- DEBUG(3,("call_trans2qfilepathinfo: fileinfo of %s failed (%s)\n",fname,strerror(errno)));
- return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
- }
if (INFO_LEVEL_IS_UNIX(info_level)) {
/* Always do lstat for UNIX calls. */
@@ -2297,7 +2302,7 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
DEBUG(3,("call_trans2qfilepathinfo: SMB_VFS_LSTAT of %s failed (%s)\n",fname,strerror(errno)));
return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
}
- } else if (!VALID_STAT(sbuf) && SMB_VFS_STAT(conn,fname,&sbuf)) {
+ } else if (SMB_VFS_STAT(conn,fname,&sbuf)) {
DEBUG(3,("call_trans2qfilepathinfo: SMB_VFS_STAT of %s failed (%s)\n",fname,strerror(errno)));
return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
}
@@ -2329,7 +2334,7 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
DEBUG(3,("call_trans2qfilepathinfo: TRANSACT2_QPATHINFO: level = %d\n", info_level));
- srvstr_get_path(inbuf, fname, &params[6], sizeof(fname), -1, STR_TERMINATE, &status);
+ srvstr_get_path(inbuf, fname, &params[6], sizeof(fname), -1, STR_TERMINATE, &status, False);
if (!NT_STATUS_IS_OK(status)) {
return ERROR_NT(status);
}
@@ -2337,6 +2342,9 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
unix_convert(fname,conn,0,&bad_path,&sbuf);
+ if (bad_path) {
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ }
if (!check_name(fname,conn)) {
DEBUG(3,("call_trans2qfilepathinfo: fileinfo of %s failed (%s)\n",fname,strerror(errno)));
return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
@@ -2871,7 +2879,6 @@ NTSTATUS hardlink_internals(connection_struct *conn, char *oldname, char *newnam
BOOL bad_path_oldname = False;
BOOL bad_path_newname = False;
SMB_STRUCT_STAT sbuf1, sbuf2;
- BOOL rc, rcdest;
pstring last_component_oldname;
pstring last_component_newname;
NTSTATUS status = NT_STATUS_OK;
@@ -2884,8 +2891,8 @@ NTSTATUS hardlink_internals(connection_struct *conn, char *oldname, char *newnam
return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
}
- rc = unix_convert(oldname,conn,last_component_oldname,&bad_path_oldname,&sbuf1);
- if (!rc && bad_path_oldname) {
+ unix_convert(oldname,conn,last_component_oldname,&bad_path_oldname,&sbuf1);
+ if (bad_path_oldname) {
return NT_STATUS_OBJECT_PATH_NOT_FOUND;
}
@@ -2905,8 +2912,8 @@ NTSTATUS hardlink_internals(connection_struct *conn, char *oldname, char *newnam
return NT_STATUS_ACCESS_DENIED;
}
- rcdest = unix_convert(newname,conn,last_component_newname,&bad_path_newname,&sbuf2);
- if (!rcdest && bad_path_newname) {
+ unix_convert(newname,conn,last_component_newname,&bad_path_newname,&sbuf2);
+ if (bad_path_newname) {
return NT_STATUS_OBJECT_PATH_NOT_FOUND;
}
@@ -2974,6 +2981,8 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
if (!params)
return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+ ZERO_STRUCT(sbuf);
+
if (tran_call == TRANSACT2_SETFILEINFO) {
if (total_params < 4)
return(ERROR_DOS(ERRDOS,ERRinvalidparam));
@@ -2988,8 +2997,7 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
* to do this call. JRA.
*/
pstrcpy(fname, fsp->fsp_name);
- unix_convert(fname,conn,0,&bad_path,&sbuf);
- if (!check_name(fname,conn) || (!VALID_STAT(sbuf))) {
+ if (SMB_VFS_STAT(conn,fname,&sbuf) != 0) {
DEBUG(3,("call_trans2setfilepathinfo: fileinfo of %s failed (%s)\n",fname,strerror(errno)));
return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
}
@@ -3027,11 +3035,14 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
return(ERROR_DOS(ERRDOS,ERRinvalidparam));
info_level = SVAL(params,0);
- srvstr_get_path(inbuf, fname, &params[6], sizeof(fname), -1, STR_TERMINATE, &status);
+ srvstr_get_path(inbuf, fname, &params[6], sizeof(fname), -1, STR_TERMINATE, &status, False);
if (!NT_STATUS_IS_OK(status)) {
return ERROR_NT(status);
}
unix_convert(fname,conn,0,&bad_path,&sbuf);
+ if (bad_path) {
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ }
/*
* For CIFS UNIX extensions the target name may not exist.
@@ -3205,7 +3216,7 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
SET_OPEN_MODE(DOS_OPEN_RDWR),
(FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
FILE_ATTRIBUTE_NORMAL,
- 0, &access_mode, &action);
+ INTERNAL_OPEN_ONLY, &access_mode, &action);
if (new_fsp == NULL)
return(UNIXERROR(ERRDOS,ERRbadpath));
@@ -3374,14 +3385,15 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n",
return(ERROR_DOS(ERRDOS,ERRnoaccess));
if (file_type != UNIX_TYPE_CHARDEV && file_type != UNIX_TYPE_BLKDEV &&
- file_type != UNIX_TYPE_FIFO)
+ file_type != UNIX_TYPE_FIFO &&
+ file_type != UNIX_TYPE_SOCKET)
return(ERROR_DOS(ERRDOS,ERRnoaccess));
DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_BASIC doing mknod dev %.0f mode \
0%o for file %s\n", (double)dev, unixmode, fname ));
/* Ok - do the mknod. */
- if (SMB_VFS_MKNOD(conn,dos_to_unix_static(fname), unixmode, dev) != 0)
+ if (SMB_VFS_MKNOD(conn,fname, unixmode, dev) != 0)
return(UNIXERROR(ERRDOS,ERRnoaccess));
inherit_access_acl(conn, fname, unixmode);
@@ -3482,7 +3494,7 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n",
char *newname = fname;
/* Set a hard link. */
- srvstr_get_path(inbuf, oldname, pdata, sizeof(oldname), -1, STR_TERMINATE, &status);
+ srvstr_get_path(inbuf, oldname, pdata, sizeof(oldname), -1, STR_TERMINATE, &status, False);
if (!NT_STATUS_IS_OK(status)) {
return ERROR_NT(status);
}
@@ -3515,7 +3527,7 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n",
overwrite = (CVAL(pdata,0) ? True : False);
root_fid = IVAL(pdata,4);
len = IVAL(pdata,8);
- srvstr_get_path(inbuf, newname, &pdata[12], sizeof(newname), len, 0, &status);
+ srvstr_get_path(inbuf, newname, &pdata[12], sizeof(newname), len, 0, &status, False);
if (!NT_STATUS_IS_OK(status)) {
return ERROR_NT(status);
}
@@ -3538,7 +3550,7 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n",
if (fsp) {
DEBUG(10,("call_trans2setfilepathinfo: SMB_FILE_RENAME_INFORMATION (fnum %d) %s -> %s\n",
fsp->fnum, fsp->fsp_name, base_name ));
- status = rename_internals_fsp(conn, fsp, base_name, overwrite);
+ status = rename_internals_fsp(conn, fsp, base_name, 0, overwrite);
} else {
DEBUG(10,("call_trans2setfilepathinfo: SMB_FILE_RENAME_INFORMATION %s -> %s\n",
fname, newname ));
@@ -3654,7 +3666,7 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n",
SET_OPEN_MODE(DOS_OPEN_RDWR),
(FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
FILE_ATTRIBUTE_NORMAL,
- 0, &access_mode, &action);
+ INTERNAL_OPEN_ONLY, &access_mode, &action);
if (new_fsp == NULL)
return(UNIXERROR(ERRDOS,ERRbadpath));
@@ -3695,7 +3707,7 @@ static int call_trans2mkdir(connection_struct *conn,
if (total_params < 4)
return(ERROR_DOS(ERRDOS,ERRinvalidparam));
- srvstr_get_path(inbuf, directory, &params[4], sizeof(directory), -1, STR_TERMINATE, &status);
+ srvstr_get_path(inbuf, directory, &params[4], sizeof(directory), -1, STR_TERMINATE, &status, False);
if (!NT_STATUS_IS_OK(status)) {
return ERROR_NT(status);
}
@@ -3703,6 +3715,9 @@ static int call_trans2mkdir(connection_struct *conn,
DEBUG(3,("call_trans2mkdir : name = %s\n", directory));
unix_convert(directory,conn,0,&bad_path,&sbuf);
+ if (bad_path) {
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ }
if (check_name(directory,conn))
ret = vfs_MkDir(conn,directory,unix_mode(conn,aDIR,directory));
diff --git a/source/smbd/uid.c b/source/smbd/uid.c
index e1864c74caa..de2f96450fc 100644
--- a/source/smbd/uid.c
+++ b/source/smbd/uid.c
@@ -125,6 +125,13 @@ static BOOL check_user_ok(connection_struct *conn, user_struct *vuser,int snum)
readonly_share = is_share_read_only_for_user(conn, vuser);
+ if (!readonly_share &&
+ !share_access_check(conn, snum, vuser, FILE_WRITE_DATA)) {
+ /* smb.conf allows r/w, but the security descriptor denies
+ * write. Fall back to looking at readonly. */
+ readonly_share = True;
+ }
+
if (!share_access_check(conn, snum, vuser, readonly_share ? FILE_READ_DATA : FILE_WRITE_DATA)) {
return False;
}
diff --git a/source/smbd/utmp.c b/source/smbd/utmp.c
index a521d0113d4..b1735dfcc49 100644
--- a/source/smbd/utmp.c
+++ b/source/smbd/utmp.c
@@ -514,10 +514,10 @@ static BOOL sys_utmp_fill(struct utmp *u,
* But note that we do the more precise ut_tv as the final assignment.
*/
#if defined(HAVE_UT_UT_TIME)
- gettimeofday(&timeval, NULL);
+ GetTimeOfDay(&timeval);
u->ut_time = timeval.tv_sec;
#elif defined(HAVE_UT_UT_TV)
- gettimeofday(&timeval, NULL);
+ GetTimeOfDay(&timeval);
u->ut_tv = timeval;
#else
#error "with-utmp must have UT_TIME or UT_TV"
diff --git a/source/smbd/vfs.c b/source/smbd/vfs.c
index a415e0470e2..13cfdac0f35 100644
--- a/source/smbd/vfs.c
+++ b/source/smbd/vfs.c
@@ -784,6 +784,31 @@ char *vfs_GetWd(connection_struct *conn, char *path)
return (path);
}
+BOOL canonicalize_path(connection_struct *conn, pstring path)
+{
+#ifdef REALPATH_TAKES_NULL
+ char *resolved_name = SMB_VFS_REALPATH(conn,path,NULL);
+ if (!resolved_name) {
+ return False;
+ }
+ pstrcpy(path, resolved_name);
+ SAFE_FREE(resolved_name);
+ return True;
+#else
+#ifdef PATH_MAX
+ char resolved_name_buf[PATH_MAX+1];
+#else
+ pstring resolved_name_buf;
+#endif
+ char *resolved_name = SMB_VFS_REALPATH(conn,path,resolved_name_buf);
+ if (!resolved_name) {
+ return False;
+ }
+ pstrcpy(path, resolved_name);
+ return True;
+#endif /* REALPATH_TAKES_NULL */
+}
+
/*******************************************************************
Reduce a file name, removing .. elements and checking that
it is below dir in the heirachy. This uses realpath.
@@ -804,6 +829,7 @@ BOOL reduce_name(connection_struct *conn, pstring fname)
char *resolved_name = NULL;
size_t con_path_len = strlen(conn->connectpath);
char *p = NULL;
+ int saved_errno = errno;
DEBUG(3,("reduce_name [%s] [%s]\n", fname, conn->connectpath));
@@ -817,6 +843,7 @@ BOOL reduce_name(connection_struct *conn, pstring fname)
switch (errno) {
case ENOTDIR:
DEBUG(3,("reduce_name: Component not a directory in getting realpath for %s\n", fname));
+ errno = saved_errno;
return False;
case ENOENT:
{
@@ -841,6 +868,7 @@ BOOL reduce_name(connection_struct *conn, pstring fname)
#endif
if (!resolved_name) {
DEBUG(3,("reduce_name: couldn't get realpath for %s\n", fname));
+ errno = saved_errno;
return False;
}
pstrcpy(tmp_fname, resolved_name);
@@ -851,6 +879,7 @@ BOOL reduce_name(connection_struct *conn, pstring fname)
resolved_name = strdup(tmp_fname);
if (!resolved_name) {
DEBUG(0,("reduce_name: malloc fail for %s\n", tmp_fname));
+ errno = saved_errno;
return False;
}
#else
@@ -865,6 +894,7 @@ BOOL reduce_name(connection_struct *conn, pstring fname)
}
default:
DEBUG(1,("reduce_name: couldn't get realpath for %s\n", fname));
+ errno = saved_errno;
return False;
}
}
@@ -875,13 +905,15 @@ BOOL reduce_name(connection_struct *conn, pstring fname)
DEBUG(0,("reduce_name: realpath doesn't return absolute paths !\n"));
if (free_resolved_name)
SAFE_FREE(resolved_name);
+ errno = saved_errno;
return False;
}
if (strncmp(conn->connectpath, resolved_name, con_path_len) != 0) {
- DEBUG(2, ("reduce_name: Bad access attemt: %s is a symlink outside the share path", fname));
+ DEBUG(2, ("reduce_name: Bad access attempt: %s is a symlink outside the share path", fname));
if (free_resolved_name)
SAFE_FREE(resolved_name);
+ errno = EACCES;
return False;
}
@@ -900,11 +932,13 @@ BOOL reduce_name(connection_struct *conn, pstring fname)
DEBUG(3,("reduce_name: denied: file path name %s is a symlink\n",fname));
if (free_resolved_name)
SAFE_FREE(resolved_name);
+ errno = EACCES;
return False;
}
DEBUG(3,("reduce_name: %s reduced to %s\n", fname, p));
if (free_resolved_name)
SAFE_FREE(resolved_name);
+ errno = saved_errno;
return(True);
}