diff options
27 files changed, 590 insertions, 320 deletions
diff --git a/README b/README deleted file mode 100644 index 5501998ef1..0000000000 --- a/README +++ /dev/null @@ -1,140 +0,0 @@ -Readme for Samba 4 -================== - -Samba 4 is the ambitious next version of the Samba suite that is being -developed in parallel to the stable 3.0 series. The main emphasis in -this branch is support for the Active Directory logon protocols used -by Windows 2000 and above. - -While we welcome your interest in Samba 4, we don't want you to run your -network with it quite yet. Please note the WARNINGS below, and the -STATUS file, which aims to document what should and should not work. - -With 4 years of development under our belt since Tridge first proposed -a new Virtual File System (VFS) layer for Samba3 (a project which -eventually lead to our Active Directory efforts), we felt that we -should create something we could 'show off' to our users. This is a -Technology Preview (TP), aimed at allowing you, our users, managers and -developers to see how we have progressed, and to invite your feedback and -support. - -Warnings --------- - -Samba4 is currently at alpha stage. That is more a reference to -Samba4's lack of the features we expect you will need than a statement -of code quality, but clearly it hasn't seen a broad deployment yet. If -you were to upgrade Samba3 (or indeed Windows) to Samba4, you would find -many things work, but that other key features you may have relied on -simply are not there yet. - -For example, while Samba 3.0 is an excellent member of a Active -Directory domain, Samba4 is happier as a domain controller: (This is -where we have done most of the research and development). - -While Samba4 is subjected to an awesome battery of tests on an -automated basis, and we have found Samba4 to be very stable in it's -behaviour, we have to recommend against upgrading production servers -from Samba 3 to Samba 4 at this stage. If you are upgrading an -experimental server, or looking to develop and test Samba, you should -backup all configuration and data. - -As we research the needs of Active Directory integration more closely, -we may need to change the format of the user database, in particular -as we begin to understand how the attributes are generated and stored. -At a worst case, we expect users will be able to extract the stored -data as LDIF and hand munge it, but until we make an alpha release, we -won't do this automatically. Indeed, many module changes are simply -easier to cope with if you just re-provision after the upgrade. - -We value the security of your computers, and so we must warn you that -Samba 4 Technology Preview includes basic Access Control List (ACL) -protection on the main user database, but due to time constraints, -none on the registry at this stage. We also do not currently have -ACLs on the SWAT web-based management tool. This means that Samba 4 -Technology Preview is not secure, and should not be exposed to -untrusted networks. - -Within the above proviso, file system access should occur as the -logged in user, much as Samba3 does. - -As such, we must strongly recommend against using Samba4 in a -production environment at this stage. - -New Features ------------- - -Samba4 supports the server-side of the Active Directory logon environment -used by Windows 2000 and later, so we can do full domain join -and domain logon operations with these clients. - -Our Domain Controller (DC) implementation includes our own built-in -LDAP server and Kerberos Key Distribution Centre (KDC) as well as the -Samba3-like logon services provided over CIFS. We correctly generate -the infamous Kerberos PAC, and include it with the Kerberos tickets we -issue. - -SWAT is now integrated into Samba 4 as the user-friendly interface to -Samba server management. SWAT provides easy access to our -setup and migration tools. Using SWAT, you can migrate windows -domains in Samba 4, allowing easy setup of initial user databases, and -upgrades from Samba 3. - -The new VFS features in Samba 4 adapts the file-system on the server to -match the Windows client semantics, allowing Samba 4 to better match -windows behaviour and application expectations. This includes file -annotation information (in streams) and NT ACLs in particular. The -VFS is backed with an extensive automated test suite. - -A new scripting interface has been added to Samba 4, allowing -JavaScript programs to interface to Samba's internals. - -The Samba 4 architecture is based around an LDAP-like database that -can use a range of modular backends. One of the backends supports -standards compliant LDAP servers (including OpenLDAP), and we are -working on modules to map between AD-like behaviours and this back-end. -We are aiming for Samba 4 to be powerful front-end to large -directories. - -Changes -------- - -Those familiar with Samba 3 can find a list of user-visible changes -since that release series in the NEWS file. - -- An optional password is no longer supported as the second argument to - smbclient. - -- The default location of smb.conf in non-FHS builds has changed from the - PREFIX/lib directory to the PREFIX/etc directory. - -Known issues ------------- - -- Standalone server and domain member roles are not currently - supported. While we have much of the infrastructure required, we - have not collected these pieces together. - -- There is no printing support in the current release. - -- SWAT can be painful with <TAB> and forms. Just use the mouse, as - the JavaScript layer doing this will change. - -Running Samba4 --------------- - -A short guide to setting up Samba 4 can be found in the howto.txt file -in root of the tarball. - -Development and feedback ------------------------- - -Bugs can be filed at https://bugzilla.samba.org/. Please -look at the STATUS file before filing a bug to see if a particular -is supposed to work yet. - -Development and general discussion about Samba 4 happens mainly on -the #samba-technical IRC channel (on irc.freenode.net) and -the samba-technical mailing list (see http://lists.samba.org/ for -details). - diff --git a/WHATSNEW.txt b/WHATSNEW.txt index f58df497f5..e4c4ea8087 100644 --- a/WHATSNEW.txt +++ b/WHATSNEW.txt @@ -1,4 +1,4 @@ -What's new in Samba 4 alpha3 +What's new in Samba 4 alpha4 ============================ Samba 4 is the ambitious next version of the Samba suite that is being @@ -10,14 +10,13 @@ Samba 4 is currently not yet in a state where it is usable in production environments. Note the WARNINGS below, and the STATUS file, which aims to document what should and should not work. -Samba4 alpha3 follows on from our second alpha release (made in -December), the first alpha release (made in September), and the -Technology Preview series we have offered for some time now. +Samba4 alpha4 follows on from the alpha release series we have been +publishing since September last year. WARNINGS ======== -Samba4 alpha3 is not a final Samba release. That is more a reference +Samba4 alpha4 is not a final Samba release. That is more a reference to Samba4's lack of the features we expect you will need than a statement of code quality, but clearly it hasn't seen a broad deployment yet. If you were to upgrade Samba3 (or indeed Windows) to @@ -64,43 +63,47 @@ working on modules to map between AD-like behaviours and this backend. We are aiming for Samba 4 to be powerful frontend to large directories. -CHANGES SINCE Alpha2 +CHANGES SINCE Alpha3 ===================== In the time since Samba4 Alpha2 was released in December 2007, Samba has continued to evolve, but you may particularly notice these areas: - Python Bindings: Bindings for Python are now in place, and used for - Samba's provision script, slowly displacing EJS as the embedded - scripting language. With its increased use, Python is no longer - optional, and configure will generate an error if it cannot locate - an appropriate Python installation. + Python Bindings: Bindings for Python are now used for all internal + scripting, and the system python installation is used to run all + Samba python scripts (in place of smbpython found in the previous + alpha). - SWAT Disabled: Due to a lack of developer time and without a - long-term web developer to maintain it, the SWAT web UI has been - disabled. + As such Python is no longer optional, and configure will generate an + error if it cannot locate an appropriate Python installation. - Oplock support: Samba4's file server now supports oplocks + SWAT Remains Disabled: Due to a lack of developer time and without a + long-term web developer to maintain it, the SWAT web UI remains been + disabled (and would need to be rewritten in python in any case). GNU Make: To try and simplfy our build system, we rely on GNU Make to avoid autogenerating a massive single makefile. - Account Expiry: Samba4 now better handles installations over 30 days - old (thanks to our long-suffering testers for keeping installations - around that long!) + Registry: Samba4's registry library has continued to improve. - Registry: Samba4 registry interoperability has been improved in - both the client utilities and in the registry service exposed by - the Samba4 server itself. + ID mapping: Samba4 uses the internal ID mapping in winbind for all + but a few core users. Samba users should not appear in /etc/passwd, + as Samba will generate new user and group IDs regradless. - Administrative Tools: Many enhancements have been made that allow - better integration with Windows administrative tools, especially - Active Directory Users and Computers. + NTP: Samba4 can act as a signing server for the ntp.org NTP deamon, + allowing NTPd to reply using Microsoft's non-standard signing + scheme. A patch to make NTPd talk to Samba for this purpose has + been submitted to the ntp.org project. - ID mapping: Samba4 now handles ID mapping via winbind. The mappings - are stored in a central ldb that could be shared across multiple - machines using LDAP. Internal callers access this interface via a new - wbclient library. + CLDAP: Users should experience less arbitary delays and more success with + group policy, domain joins and logons due to an improved + implementation of CLDAP and the 'netlogon' mailslot datagrams. + + SMB2: The Samba4 SMB2 server and testsuite have been greatly + improved, but the SMB2 server remains off by default. + + Secure DNS update: Configuration for GSS-TSIG updates of DNS records + is now generated by the provision script. These are just some of the highlights of the work done in the past few months. More details can be found in our GIT history. @@ -120,12 +123,22 @@ KNOWN ISSUES - There is no printing support in the current release. +- There is no netbios browsing support in the current release + - The Samba4 port of the CTDB clustering support is not yet complete - Clock Synchronisation is critical. Many 'wrong password' errors are actually due to Kerberos objecting to a clock skew between client - and server. + and server. (The NTP work is partly to assist with this problem). + +- Samba4 alpha4 is currently only portable to recent Linux + distributions. Work to return support for other Unix varients is + expected during the next alpha cycle +- Samba4 alpha4 is incompatible with GnuTLS 2.0, found in Fedora 9 and + recent Ubuntu releases. Please remove the + gnutls-devel/libgnutls-dev package before compiling (otherwise 'make + test' and LDAPS operations will hang). RUNNING Samba4 ============== diff --git a/source4/VERSION b/source4/VERSION index d40eba4e8a..7a526c625e 100644 --- a/source4/VERSION +++ b/source4/VERSION @@ -57,7 +57,7 @@ SAMBA_VERSION_TP_RELEASE= # e.g. SAMBA_VERSION_ALPHA_RELEASE=1 # # -> "4.0.0alpha1" # ######################################################## -SAMBA_VERSION_ALPHA_RELEASE=4 +SAMBA_VERSION_ALPHA_RELEASE=5 ######################################################## # For 'pre' releases the version will be # diff --git a/source4/cluster/ctdb/opendb_ctdb.c b/source4/cluster/ctdb/opendb_ctdb.c index ed09cf0bbc..b1faf9e0e6 100644 --- a/source4/cluster/ctdb/opendb_ctdb.c +++ b/source4/cluster/ctdb/opendb_ctdb.c @@ -283,7 +283,8 @@ static NTSTATUS odb_oplock_break_send(struct odb_context *odb, struct opendb_ent */ static NTSTATUS odb_ctdb_open_file(struct odb_lock *lck, void *file_handle, const char *path, - int *fd, bool allow_level_II_oplock, + int *fd, NTTIME open_write_time, + bool allow_level_II_oplock, uint32_t oplock_level, uint32_t *oplock_granted) { @@ -492,37 +493,30 @@ static NTSTATUS odb_ctdb_set_delete_on_close(struct odb_lock *lck, bool del_on_c return odb_push_record(lck, &file); } +static NTSTATUS odb_ctdb_set_write_time(struct odb_lock *lck, + NTTIME write_time, bool force) +{ + /* + * as this file will went away and isn't used yet, + * copy the implementation from the tdb backend + * --metze + */ + return NT_STATUS_FOOBAR; +} + /* return the current value of the delete_on_close bit, and how many people still have the file open */ -static NTSTATUS odb_ctdb_get_delete_on_close(struct odb_context *odb, - DATA_BLOB *key, bool *del_on_close) +static NTSTATUS odb_ctdb_get_file_infos(struct odb_context *odb, DATA_BLOB *key, + bool *del_on_close, NTTIME *write_time) { - NTSTATUS status; - struct opendb_file file; - struct odb_lock *lck; - - (*del_on_close) = false; - - lck = odb_lock(odb, odb, key); - NT_STATUS_HAVE_NO_MEMORY(lck); - - status = odb_pull_record(lck, &file); - if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) { - talloc_free(lck); - return NT_STATUS_OK; - } - if (!NT_STATUS_IS_OK(status)) { - talloc_free(lck); - return status; - } - - (*del_on_close) = file.delete_on_close; - - talloc_free(lck); - - return NT_STATUS_OK; + /* + * as this file will went away and isn't used yet, + * copy the implementation from the tdb backend + * --metze + */ + return NT_STATUS_FOOBAR; } @@ -589,7 +583,8 @@ static const struct opendb_ops opendb_ctdb_ops = { .odb_rename = odb_ctdb_rename, .odb_get_path = odb_ctdb_get_path, .odb_set_delete_on_close = odb_ctdb_set_delete_on_close, - .odb_get_delete_on_close = odb_ctdb_get_delete_on_close, + .odb_set_write_time = odb_ctdb_set_write_time, + .odb_get_file_infos = odb_ctdb_get_file_infos, .odb_can_open = odb_ctdb_can_open, .odb_update_oplock = odb_ctdb_update_oplock, .odb_break_oplocks = odb_ctdb_break_oplocks diff --git a/source4/heimdal/lib/gssapi/krb5/init_sec_context.c b/source4/heimdal/lib/gssapi/krb5/init_sec_context.c index d4482a54b2..ab7624eef0 100644 --- a/source4/heimdal/lib/gssapi/krb5/init_sec_context.c +++ b/source4/heimdal/lib/gssapi/krb5/init_sec_context.c @@ -540,12 +540,18 @@ init_auth goto failure; } - ret = _gsskrb5_encapsulate (minor_status, &outbuf, output_token, - (u_char *)"\x01\x00", GSS_KRB5_MECHANISM); - if (ret) - goto failure; + if (flags & GSS_C_DCE_STYLE) { + output_token->value = outbuf.data; + output_token->length = outbuf.length; + } else { + ret = _gsskrb5_encapsulate (minor_status, &outbuf, output_token, + (u_char *)"\x01\x00", GSS_KRB5_MECHANISM); + if (ret) + goto failure; + + krb5_data_free (&outbuf); + } - krb5_data_free (&outbuf); krb5_free_creds(context, kcred); free_Checksum(&cksum); if (cred == NULL) diff --git a/source4/lib/ldb/tests/python/ldap.py b/source4/lib/ldb/tests/python/ldap.py index c76222c207..aba9581ec5 100755 --- a/source4/lib/ldb/tests/python/ldap.py +++ b/source4/lib/ldb/tests/python/ldap.py @@ -14,7 +14,7 @@ from samba.auth import system_session from ldb import (SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, LDB_ERR_NO_SUCH_OBJECT, LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS, LDB_ERR_ENTRY_ALREADY_EXISTS, LDB_ERR_UNWILLING_TO_PERFORM, - LDB_ERR_NOT_ALLOWED_ON_NON_LEAF, LDB_ERR_OTHER) + LDB_ERR_NOT_ALLOWED_ON_NON_LEAF, LDB_ERR_OTHER, LDB_ERR_INVALID_DN_SYNTAX) from samba import Ldb from subunit import SubunitTestRunner from samba import param @@ -115,6 +115,86 @@ class BasicTests(unittest.TestCase): "userAccountControl": "4096", "displayname": "ldap testy"}) + self.delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn) + try: + ldb.add({"dn": "cn=ldaptestcomputer3,cn=computers," + self.base_dn, + "objectClass": "computer", + "cn": "LDAPtest2COMPUTER" + }) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, LDB_ERR_INVALID_DN_SYNTAX) + + self.delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn) + try: + ldb.add({"dn": "cn=ldaptestcomputer3,cn=computers," + self.base_dn, + "objectClass": "computer", + "cn": "ldaptestcomputer3", + "sAMAccountType": "805306368" + }) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, LDB_ERR_UNWILLING_TO_PERFORM) + + self.delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn) + try: + ldb.add({"dn": "cn=ldaptestcomputer3,cn=computers," + self.base_dn, + "objectClass": "computer", + "cn": "ldaptestcomputer3", + "userAccountControl": "0" + }) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, LDB_ERR_UNWILLING_TO_PERFORM) + + self.delete_force(self.ldb, "cn=ldaptestuser7,cn=users," + self.base_dn) + try: + ldb.add({"dn": "cn=ldaptestuser7,cn=users," + self.base_dn, + "objectClass": "user", + "cn": "LDAPtestuser7", + "userAccountControl": "0" + }) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, LDB_ERR_UNWILLING_TO_PERFORM) + + self.delete_force(self.ldb, "cn=ldaptestuser7,cn=users," + self.base_dn) + + ldb.add({"dn": "cn=ldaptestuser7,cn=users," + self.base_dn, + "objectClass": "user", + "cn": "LDAPtestuser7", + "userAccountControl": "2" + }) + + self.delete_force(self.ldb, "cn=ldaptestuser7,cn=users," + self.base_dn) + + self.delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn) + ldb.add({"dn": "cn=ldaptestcomputer3,cn=computers," + self.base_dn, + "objectClass": "computer", + "cn": "LDAPtestCOMPUTER3" + }) + + print "Testing ldb.search for (&(cn=ldaptestcomputer3)(objectClass=user))"; + res = ldb.search(self.base_dn, expression="(&(cn=ldaptestcomputer3)(objectClass=user))"); + self.assertEquals(len(res), 1, "Found only %d for (&(cn=ldaptestcomputer3)(objectClass=user))" % len(res)) + + self.assertEquals(str(res[0].dn), ("CN=ldaptestcomputer3,CN=Computers," + self.base_dn)); + self.assertEquals(res[0]["cn"][0], "ldaptestcomputer3"); + self.assertEquals(res[0]["name"][0], "ldaptestcomputer3"); + self.assertEquals(res[0]["objectClass"][0], "top"); + self.assertEquals(res[0]["objectClass"][1], "person"); + self.assertEquals(res[0]["objectClass"][2], "organizationalPerson"); + self.assertEquals(res[0]["objectClass"][3], "user"); + self.assertEquals(res[0]["objectClass"][4], "computer"); + self.assertTrue("objectGUID" in res[0]) + self.assertTrue("whenCreated" in res[0]) + self.assertEquals(res[0]["objectCategory"][0], ("CN=Computer,CN=Schema,CN=Configuration," + self.base_dn)); + self.assertEquals(int(res[0]["primaryGroupID"][0]), 513); + self.assertEquals(int(res[0]["sAMAccountType"][0]), 805306368); + self.assertEquals(int(res[0]["userAccountControl"][0]), 546); + + self.delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn) + print "Testing attribute or value exists behaviour" try: ldb.modify_ldif(""" @@ -125,34 +205,36 @@ servicePrincipalName: host/ldaptest2computer servicePrincipalName: host/ldaptest2computer servicePrincipalName: cifs/ldaptest2computer """) + self.fail() except LdbError, (num, msg): self.assertEquals(num, LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS) - ldb.modify_ldif(""" + ldb.modify_ldif(""" dn: cn=ldaptest2computer,cn=computers,""" + self.base_dn + """ changetype: modify replace: servicePrincipalName servicePrincipalName: host/ldaptest2computer servicePrincipalName: cifs/ldaptest2computer """) - try: - ldb.modify_ldif(""" + try: + ldb.modify_ldif(""" dn: cn=ldaptest2computer,cn=computers,""" + self.base_dn + """ changetype: modify add: servicePrincipalName servicePrincipalName: host/ldaptest2computer """) - except LdbError, (num, msg): - self.assertEquals(num, LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS) - - print "Testing ranged results" - ldb.modify_ldif(""" + self.fail() + except LdbError, (num, msg): + self.assertEquals(num, LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS) + + print "Testing ranged results" + ldb.modify_ldif(""" dn: cn=ldaptest2computer,cn=computers,""" + self.base_dn + """ changetype: modify replace: servicePrincipalName """) - ldb.modify_ldif(""" + ldb.modify_ldif(""" dn: cn=ldaptest2computer,cn=computers,""" + self.base_dn + """ changetype: modify add: servicePrincipalName @@ -188,53 +270,53 @@ servicePrincipalName: host/ldaptest2computer28 servicePrincipalName: host/ldaptest2computer29 """) - res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, - attrs=["servicePrincipalName;range=0-*"]) - self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") - #print len(res[0]["servicePrincipalName;range=0-*"]) - self.assertEquals(len(res[0]["servicePrincipalName;range=0-*"]), 30) + res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, + attrs=["servicePrincipalName;range=0-*"]) + self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") + #print len(res[0]["servicePrincipalName;range=0-*"]) + self.assertEquals(len(res[0]["servicePrincipalName;range=0-*"]), 30) - res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=0-19"]) - self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") + res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=0-19"]) + self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") # print res[0]["servicePrincipalName;range=0-19"].length - self.assertEquals(len(res[0]["servicePrincipalName;range=0-19"]), 20) + self.assertEquals(len(res[0]["servicePrincipalName;range=0-19"]), 20) - res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=0-30"]) - self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") - self.assertEquals(len(res[0]["servicePrincipalName;range=0-*"]), 30) + res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=0-30"]) + self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") + self.assertEquals(len(res[0]["servicePrincipalName;range=0-*"]), 30) - res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=0-40"]) - self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") - self.assertEquals(len(res[0]["servicePrincipalName;range=0-*"]), 30) + res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=0-40"]) + self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") + self.assertEquals(len(res[0]["servicePrincipalName;range=0-*"]), 30) - res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=30-40"]) - self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") - self.assertEquals(len(res[0]["servicePrincipalName;range=30-*"]), 0) + res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=30-40"]) + self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") + self.assertEquals(len(res[0]["servicePrincipalName;range=30-*"]), 0) - res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=10-40"]) - self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") - self.assertEquals(len(res[0]["servicePrincipalName;range=10-*"]), 20) - # pos_11 = res[0]["servicePrincipalName;range=10-*"][18] - - res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=11-40"]) - self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") - self.assertEquals(len(res[0]["servicePrincipalName;range=11-*"]), 19) + res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=10-40"]) + self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") + self.assertEquals(len(res[0]["servicePrincipalName;range=10-*"]), 20) + # pos_11 = res[0]["servicePrincipalName;range=10-*"][18] + + res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=11-40"]) + self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") + self.assertEquals(len(res[0]["servicePrincipalName;range=11-*"]), 19) # print res[0]["servicePrincipalName;range=11-*"][18] # print pos_11 # self.assertEquals((res[0]["servicePrincipalName;range=11-*"][18]), pos_11) - res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=11-15"]) - self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") - self.assertEquals(len(res[0]["servicePrincipalName;range=11-15"]), 5) + res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=11-15"]) + self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") + self.assertEquals(len(res[0]["servicePrincipalName;range=11-15"]), 5) # self.assertEquals(res[0]["servicePrincipalName;range=11-15"][4], pos_11) - res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName"]) - self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") + res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName"]) + self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") # print res[0]["servicePrincipalName"][18] # print pos_11 - self.assertEquals(len(res[0]["servicePrincipalName"]), 30) + self.assertEquals(len(res[0]["servicePrincipalName"]), 30) # self.assertEquals(res[0]["servicePrincipalName"][18], pos_11) self.delete_force(self.ldb, "cn=ldaptestuser2,cn=users," + self.base_dn) @@ -322,6 +404,10 @@ servicePrincipalName: host/ldaptest2computer29 res = ldb.search(expression="(&(anr=not ldap user2)(objectClass=user))") self.assertEquals(len(res), 0, "Must not find (&(anr=not ldap user2)(objectClass=user))") + # Testing ldb.search for (&(anr="testy ldap")(objectClass=user)) (ie, with quotes) + res = ldb.search(expression="(&(anr==\"testy ldap\")(objectClass=user))") + self.assertEquals(len(res), 0, "Found (&(anr==\"testy ldap\")(objectClass=user))") + print "Testing Group Modifies" ldb.modify_ldif(""" dn: cn=ldaptestgroup,cn=users,""" + self.base_dn + """ @@ -361,6 +447,26 @@ member: cn=ldaptestuser3,cn=users,""" + self.base_dn + """ self.assertEquals(res[0]["cn"], "ldaptestUSER3") self.assertEquals(res[0]["name"], "ldaptestUSER3") + #"Testing ldb.search for (&(&(cn=ldaptestuser3)(userAccountControl=*))(objectClass=user))" + res = ldb.search(expression="(&(&(cn=ldaptestuser3)(userAccountControl=*))(objectClass=user))") + self.assertEquals(len(res), 1, "(&(&(cn=ldaptestuser3)(userAccountControl=*))(objectClass=user))") + + self.assertEquals(str(res[0].dn), ("CN=ldaptestUSER3,CN=Users," + self.base_dn)) + self.assertEquals(res[0]["cn"], "ldaptestUSER3") + self.assertEquals(res[0]["name"], "ldaptestUSER3") + + #"Testing ldb.search for (&(&(cn=ldaptestuser3)(userAccountControl=546))(objectClass=user))" + res = ldb.search(expression="(&(&(cn=ldaptestuser3)(userAccountControl=546))(objectClass=user))") + self.assertEquals(len(res), 1, "(&(&(cn=ldaptestuser3)(userAccountControl=546))(objectClass=user))") + + self.assertEquals(str(res[0].dn), ("CN=ldaptestUSER3,CN=Users," + self.base_dn)) + self.assertEquals(res[0]["cn"], "ldaptestUSER3") + self.assertEquals(res[0]["name"], "ldaptestUSER3") + + #"Testing ldb.search for (&(&(cn=ldaptestuser3)(userAccountControl=547))(objectClass=user))" + res = ldb.search(expression="(&(&(cn=ldaptestuser3)(userAccountControl=547))(objectClass=user))") + self.assertEquals(len(res), 0, "(&(&(cn=ldaptestuser3)(userAccountControl=547))(objectClass=user))") + # This is a Samba special, and does not exist in real AD # print "Testing ldb.search for (dn=CN=ldaptestUSER3,CN=Users," + self.base_dn + ")" # res = ldb.search("(dn=CN=ldaptestUSER3,CN=Users," + self.base_dn + ")") @@ -534,7 +640,7 @@ member: cn=ldaptestuser4,cn=ldaptestcontainer,""" + self.base_dn + """ self.assertTrue("whenCreated" in res[0]) self.assertEquals(res[0]["objectCategory"], ("CN=Person,CN=Schema,CN=Configuration," + self.base_dn)) self.assertEquals(int(res[0]["sAMAccountType"][0]), 805306368) - # self.assertEquals(res[0].userAccountControl, 546) + self.assertEquals(int(res[0]["userAccountControl"][0]), 546) self.assertEquals(res[0]["memberOf"][0], ("CN=ldaptestgroup2,CN=Users," + self.base_dn)) self.assertEquals(len(res[0]["memberOf"]), 1) @@ -578,8 +684,8 @@ member: cn=ldaptestuser4,cn=ldaptestcontainer,""" + self.base_dn + """ self.assertTrue("whenCreated" in res[0]) self.assertEquals(res[0]["objectCategory"], ("CN=Computer,CN=Schema,CN=Configuration," + self.base_dn)) self.assertEquals(int(res[0]["primaryGroupID"][0]), 513) - # self.assertEquals(res[0].sAMAccountType, 805306368) - # self.assertEquals(res[0].userAccountControl, 546) + self.assertEquals(int(res[0]["sAMAccountType"][0]), 805306368) + self.assertEquals(int(res[0]["userAccountControl"][0]), 546) self.assertEquals(res[0]["memberOf"][0], "CN=ldaptestgroup2,CN=Users," + self.base_dn) self.assertEquals(len(res[0]["memberOf"]), 1) @@ -641,7 +747,7 @@ member: cn=ldaptestuser4,cn=ldaptestcontainer,""" + self.base_dn + """ self.assertTrue("whenCreated" in res[0]) self.assertEquals(res[0]["objectCategory"][0], "CN=Computer,CN=Schema,CN=Configuration," + self.base_dn) self.assertEquals(int(res[0]["sAMAccountType"][0]), 805306369) - # self.assertEquals(res[0].userAccountControl, 4098) + self.assertEquals(int(res[0]["userAccountControl"][0]), 4096) ldb.delete(res[0].dn) diff --git a/source4/librpc/idl/opendb.idl b/source4/librpc/idl/opendb.idl index 72bf23a9b4..cdbaa6cb1b 100644 --- a/source4/librpc/idl/opendb.idl +++ b/source4/librpc/idl/opendb.idl @@ -35,6 +35,8 @@ interface opendb typedef [public] struct { boolean8 delete_on_close; + NTTIME open_write_time; + NTTIME changed_write_time; utf8string path; uint32 num_entries; opendb_entry entries[num_entries]; diff --git a/source4/ntvfs/common/opendb.c b/source4/ntvfs/common/opendb.c index 2913ea8431..6917bad52a 100644 --- a/source4/ntvfs/common/opendb.c +++ b/source4/ntvfs/common/opendb.c @@ -97,11 +97,13 @@ DATA_BLOB odb_get_key(TALLOC_CTX *mem_ctx, struct odb_lock *lck) */ NTSTATUS odb_open_file(struct odb_lock *lck, void *file_handle, const char *path, - int *fd, bool allow_level_II_oplock, + int *fd, NTTIME open_write_time, + bool allow_level_II_oplock, uint32_t oplock_level, uint32_t *oplock_granted) { return ops->odb_open_file(lck, file_handle, path, - fd, allow_level_II_oplock, + fd, open_write_time, + allow_level_II_oplock, oplock_level, oplock_granted); } @@ -159,15 +161,23 @@ NTSTATUS odb_set_delete_on_close(struct odb_lock *lck, bool del_on_close) } /* - return the current value of the delete_on_close bit, and how many - people still have the file open + update the write time on an open file */ -NTSTATUS odb_get_delete_on_close(struct odb_context *odb, - DATA_BLOB *key, bool *del_on_close) +NTSTATUS odb_set_write_time(struct odb_lock *lck, + NTTIME write_time, bool force) { - return ops->odb_get_delete_on_close(odb, key, del_on_close); + return ops->odb_set_write_time(lck, write_time, force); } +/* + return the current value of the delete_on_close bit, + and the current write time. +*/ +NTSTATUS odb_get_file_infos(struct odb_context *odb, DATA_BLOB *key, + bool *del_on_close, NTTIME *write_time) +{ + return ops->odb_get_file_infos(odb, key, del_on_close, write_time); +} /* determine if a file can be opened with the given share_access, diff --git a/source4/ntvfs/common/opendb.h b/source4/ntvfs/common/opendb.h index 045476337a..179db111ca 100644 --- a/source4/ntvfs/common/opendb.h +++ b/source4/ntvfs/common/opendb.h @@ -27,7 +27,8 @@ struct opendb_ops { DATA_BLOB (*odb_get_key)(TALLOC_CTX *mem_ctx, struct odb_lock *lck); NTSTATUS (*odb_open_file)(struct odb_lock *lck, void *file_handle, const char *path, - int *fd, bool allow_level_II_oplock, + int *fd, NTTIME open_write_time, + bool allow_level_II_oplock, uint32_t oplock_level, uint32_t *oplock_granted); NTSTATUS (*odb_open_file_pending)(struct odb_lock *lck, void *private); NTSTATUS (*odb_close_file)(struct odb_lock *lck, void *file_handle, @@ -36,8 +37,10 @@ struct opendb_ops { NTSTATUS (*odb_rename)(struct odb_lock *lck, const char *path); NTSTATUS (*odb_get_path)(struct odb_lock *lck, const char **path); NTSTATUS (*odb_set_delete_on_close)(struct odb_lock *lck, bool del_on_close); - NTSTATUS (*odb_get_delete_on_close)(struct odb_context *odb, - DATA_BLOB *key, bool *del_on_close); + NTSTATUS (*odb_set_write_time)(struct odb_lock *lck, + NTTIME write_time, bool force); + NTSTATUS (*odb_get_file_infos)(struct odb_context *odb, DATA_BLOB *key, + bool *del_on_close, NTTIME *write_time); NTSTATUS (*odb_can_open)(struct odb_lock *lck, uint32_t stream_id, uint32_t share_access, uint32_t access_mask, bool delete_on_close, diff --git a/source4/ntvfs/common/opendb_tdb.c b/source4/ntvfs/common/opendb_tdb.c index 99c0a95c20..d7531297ed 100644 --- a/source4/ntvfs/common/opendb_tdb.c +++ b/source4/ntvfs/common/opendb_tdb.c @@ -452,7 +452,8 @@ static NTSTATUS odb_tdb_open_can_internal(struct odb_context *odb, */ static NTSTATUS odb_tdb_open_file(struct odb_lock *lck, void *file_handle, const char *path, - int *fd, bool allow_level_II_oplock, + int *fd, NTTIME open_write_time, + bool allow_level_II_oplock, uint32_t oplock_level, uint32_t *oplock_granted) { struct odb_context *odb = lck->odb; @@ -474,6 +475,10 @@ static NTSTATUS odb_tdb_open_file(struct odb_lock *lck, NT_STATUS_HAVE_NO_MEMORY(lck->file.path); } + if (lck->file.open_write_time == 0) { + lck->file.open_write_time = open_write_time; + } + /* possibly grant an exclusive, batch or level2 oplock */ @@ -785,20 +790,53 @@ static NTSTATUS odb_tdb_set_delete_on_close(struct odb_lock *lck, bool del_on_cl } /* + update the write time on an open file +*/ +static NTSTATUS odb_tdb_set_write_time(struct odb_lock *lck, + NTTIME write_time, bool force) +{ + if (lck->file.path == NULL) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + if (lck->file.changed_write_time != 0 && !force) { + return NT_STATUS_OK; + } + + lck->file.changed_write_time = write_time; + + return odb_push_record(lck, &lck->file); +} + +/* return the current value of the delete_on_close bit, and how many people still have the file open */ -static NTSTATUS odb_tdb_get_delete_on_close(struct odb_context *odb, - DATA_BLOB *key, bool *del_on_close) +static NTSTATUS odb_tdb_get_file_infos(struct odb_context *odb, DATA_BLOB *key, + bool *del_on_close, NTTIME *write_time) { struct odb_lock *lck; - (*del_on_close) = false; + if (del_on_close) { + *del_on_close = false; + } + if (write_time) { + *write_time = 0; + } lck = odb_lock(odb, odb, key); NT_STATUS_HAVE_NO_MEMORY(lck); - (*del_on_close) = lck->file.delete_on_close; + if (del_on_close) { + *del_on_close = lck->file.delete_on_close; + } + if (write_time) { + if (lck->file.changed_write_time == 0) { + *write_time = lck->file.open_write_time; + } else { + *write_time = lck->file.changed_write_time; + } + } talloc_free(lck); @@ -852,7 +890,8 @@ static const struct opendb_ops opendb_tdb_ops = { .odb_rename = odb_tdb_rename, .odb_get_path = odb_tdb_get_path, .odb_set_delete_on_close = odb_tdb_set_delete_on_close, - .odb_get_delete_on_close = odb_tdb_get_delete_on_close, + .odb_set_write_time = odb_tdb_set_write_time, + .odb_get_file_infos = odb_tdb_get_file_infos, .odb_can_open = odb_tdb_can_open, .odb_update_oplock = odb_tdb_update_oplock, .odb_break_oplocks = odb_tdb_break_oplocks diff --git a/source4/ntvfs/posix/pvfs_fileinfo.c b/source4/ntvfs/posix/pvfs_fileinfo.c index 04f6ad78d0..a14c8f64ae 100644 --- a/source4/ntvfs/posix/pvfs_fileinfo.c +++ b/source4/ntvfs/posix/pvfs_fileinfo.c @@ -52,8 +52,13 @@ static uint32_t dos_mode_from_stat(struct pvfs_state *pvfs, struct stat *st) /* fill in the dos file attributes for a file */ -NTSTATUS pvfs_fill_dos_info(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd) +NTSTATUS pvfs_fill_dos_info(struct pvfs_state *pvfs, struct pvfs_filename *name, + uint_t flags, int fd) { + NTSTATUS status; + DATA_BLOB lkey; + NTTIME write_time; + /* make directories appear as size 0 with 1 link */ if (S_ISDIR(name->st.st_mode)) { name->st.st_size = 0; @@ -85,7 +90,29 @@ NTSTATUS pvfs_fill_dos_info(struct pvfs_state *pvfs, struct pvfs_filename *name, name->dos.file_id = (((uint64_t)name->st.st_dev)<<32) | name->st.st_ino; name->dos.flags = 0; - return pvfs_dosattrib_load(pvfs, name, fd); + status = pvfs_dosattrib_load(pvfs, name, fd); + NT_STATUS_NOT_OK_RETURN(status); + + if (flags & PVFS_RESOLVE_NO_OPENDB) { + return NT_STATUS_OK; + } + + status = pvfs_locking_key(name, name, &lkey); + NT_STATUS_NOT_OK_RETURN(status); + + status = odb_get_file_infos(pvfs->odb_context, &lkey, + NULL, &write_time); + data_blob_free(&lkey); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1,("WARNING: odb_get_file_infos: %s\n", nt_errstr(status))); + return status; + } + + if (!null_time(write_time)) { + name->dos.write_time = write_time; + } + + return NT_STATUS_OK; } diff --git a/source4/ntvfs/posix/pvfs_open.c b/source4/ntvfs/posix/pvfs_open.c index dada9f503f..43203086f8 100644 --- a/source4/ntvfs/posix/pvfs_open.c +++ b/source4/ntvfs/posix/pvfs_open.c @@ -280,6 +280,7 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs, f->handle->position = 0; f->handle->mode = 0; f->handle->oplock = NULL; + ZERO_STRUCT(f->handle->write_time); f->handle->open_completed = false; if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) && @@ -317,7 +318,8 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs, /* now really mark the file as open */ status = odb_open_file(lck, f->handle, name->full_name, - NULL, false, OPLOCK_NONE, NULL); + NULL, name->dos.write_time, + false, OPLOCK_NONE, NULL); if (!NT_STATUS_IS_OK(status)) { talloc_free(lck); @@ -377,7 +379,8 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs, } status = odb_open_file(lck, f->handle, name->full_name, - NULL, false, OPLOCK_NONE, NULL); + NULL, name->dos.write_time, + false, OPLOCK_NONE, NULL); if (!NT_STATUS_IS_OK(status)) { goto cleanup_delete; @@ -433,6 +436,9 @@ cleanup_delete: */ static int pvfs_handle_destructor(struct pvfs_file_handle *h) { + talloc_free(h->write_time.update_event); + h->write_time.update_event = NULL; + if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) && h->name->stream_name) { NTSTATUS status; @@ -451,6 +457,14 @@ static int pvfs_handle_destructor(struct pvfs_file_handle *h) h->fd = -1; } + if (!h->write_time.update_forced && + h->write_time.update_on_close && + h->write_time.close_time == 0) { + struct timeval tv; + tv = timeval_current(); + h->write_time.close_time = timeval_to_nttime(&tv); + } + if (h->have_opendb_entry) { struct odb_lock *lck; NTSTATUS status; @@ -462,6 +476,26 @@ static int pvfs_handle_destructor(struct pvfs_file_handle *h) return 0; } + if (h->write_time.update_forced) { + status = odb_get_file_infos(h->pvfs->odb_context, + &h->odb_locking_key, + NULL, + &h->write_time.close_time); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("Unable get write time for '%s' - %s\n", + h->name->full_name, nt_errstr(status))); + } + + h->write_time.update_forced = false; + h->write_time.update_on_close = true; + } else if (h->write_time.update_on_close) { + status = odb_set_write_time(lck, h->write_time.close_time, true); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("Unable set write time for '%s' - %s\n", + h->name->full_name, nt_errstr(status))); + } + } + status = odb_close_file(lck, h, &delete_path); if (!NT_STATUS_IS_OK(status)) { DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n", @@ -484,11 +518,26 @@ static int pvfs_handle_destructor(struct pvfs_file_handle *h) FILE_NOTIFY_CHANGE_FILE_NAME, delete_path); } + h->write_time.update_on_close = false; } talloc_free(lck); } + if (h->write_time.update_on_close) { + struct timeval tv[2]; + + nttime_to_timeval(&tv[0], h->name->dos.access_time); + nttime_to_timeval(&tv[1], h->write_time.close_time); + + if (!timeval_is_zero(&tv[0]) || !timeval_is_zero(&tv[1])) { + if (utimes(h->name->full_name, tv) == -1) { + DEBUG(0,("pvfs_handle_destructor: utimes() failed '%s' - %s\n", + h->name->full_name, strerror(errno))); + } + } + } + return 0; } @@ -594,8 +643,8 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, DATA_BLOB locking_key; status = pvfs_locking_key(parent, req, &locking_key); NT_STATUS_NOT_OK_RETURN(status); - status = odb_get_delete_on_close(pvfs->odb_context, &locking_key, - &del_on_close); + status = odb_get_file_infos(pvfs->odb_context, &locking_key, + &del_on_close, NULL); NT_STATUS_NOT_OK_RETURN(status); if (del_on_close) { return NT_STATUS_DELETE_PENDING; @@ -638,7 +687,7 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, } /* re-resolve the open fd */ - status = pvfs_resolve_name_fd(pvfs, fd, name); + status = pvfs_resolve_name_fd(pvfs, fd, name, 0); if (!NT_STATUS_IS_OK(status)) { close(fd); return status; @@ -730,10 +779,12 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, f->handle->mode = 0; f->handle->oplock = NULL; f->handle->have_opendb_entry = true; + ZERO_STRUCT(f->handle->write_time); f->handle->open_completed = false; status = odb_open_file(lck, f->handle, name->full_name, - &f->handle->fd, allow_level_II_oplock, + &f->handle->fd, name->dos.write_time, + allow_level_II_oplock, oplock_level, &oplock_granted); talloc_free(lck); if (!NT_STATUS_IS_OK(status)) { @@ -1334,6 +1385,7 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, f->handle->mode = 0; f->handle->oplock = NULL; f->handle->have_opendb_entry = false; + ZERO_STRUCT(f->handle->write_time); f->handle->open_completed = false; /* form the lock context used for byte range locking and @@ -1437,7 +1489,8 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, /* now really mark the file as open */ status = odb_open_file(lck, f->handle, name->full_name, - &f->handle->fd, allow_level_II_oplock, + &f->handle->fd, name->dos.write_time, + allow_level_II_oplock, oplock_level, &oplock_granted); if (!NT_STATUS_IS_OK(status)) { @@ -1476,7 +1529,7 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, } /* re-resolve the open fd */ - status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name); + status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name, PVFS_RESOLVE_NO_OPENDB); if (!NT_STATUS_IS_OK(status)) { talloc_free(lck); return status; @@ -1538,7 +1591,6 @@ NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs, { struct pvfs_state *pvfs = ntvfs->private_data; struct pvfs_file *f; - struct utimbuf unix_times; if (io->generic.level == RAW_CLOSE_SPLCLOSE) { return NT_STATUS_DOS(ERRSRV, ERRerror); @@ -1554,9 +1606,9 @@ NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs, } if (!null_time(io->generic.in.write_time)) { - unix_times.actime = 0; - unix_times.modtime = io->close.in.write_time; - utime(f->handle->name->full_name, &unix_times); + f->handle->write_time.update_forced = false; + f->handle->write_time.update_on_close = true; + unix_to_nt_time(&f->handle->write_time.close_time, io->generic.in.write_time); } if (io->generic.in.flags & SMB2_CLOSE_FLAGS_FULL_INFORMATION) { @@ -1915,8 +1967,8 @@ bool pvfs_delete_on_close_set(struct pvfs_state *pvfs, struct pvfs_file_handle * NTSTATUS status; bool del_on_close; - status = odb_get_delete_on_close(pvfs->odb_context, &h->odb_locking_key, - &del_on_close); + status = odb_get_file_infos(pvfs->odb_context, &h->odb_locking_key, + &del_on_close, NULL); if (!NT_STATUS_IS_OK(status)) { DEBUG(1,("WARNING: unable to determine delete on close status for open file\n")); return false; diff --git a/source4/ntvfs/posix/pvfs_rename.c b/source4/ntvfs/posix/pvfs_rename.c index 5c2a627084..d8ea5896e5 100644 --- a/source4/ntvfs/posix/pvfs_rename.c +++ b/source4/ntvfs/posix/pvfs_rename.c @@ -287,7 +287,9 @@ static NTSTATUS pvfs_rename_one(struct pvfs_state *pvfs, /* get a pvfs_filename source object */ status = pvfs_resolve_partial(pvfs, mem_ctx, - dir_path, fname1, &name1); + dir_path, fname1, + PVFS_RESOLVE_NO_OPENDB, + &name1); if (!NT_STATUS_IS_OK(status)) { goto failed; } @@ -306,7 +308,9 @@ static NTSTATUS pvfs_rename_one(struct pvfs_state *pvfs, /* get a pvfs_filename dest object */ status = pvfs_resolve_partial(pvfs, mem_ctx, - dir_path, fname2, &name2); + dir_path, fname2, + PVFS_RESOLVE_NO_OPENDB, + &name2); if (NT_STATUS_IS_OK(status)) { status = pvfs_can_delete(pvfs, req, name2, NULL); if (!NT_STATUS_IS_OK(status)) { diff --git a/source4/ntvfs/posix/pvfs_resolve.c b/source4/ntvfs/posix/pvfs_resolve.c index 2e97925c49..0f19788b97 100644 --- a/source4/ntvfs/posix/pvfs_resolve.c +++ b/source4/ntvfs/posix/pvfs_resolve.c @@ -57,7 +57,9 @@ static int component_compare(struct pvfs_state *pvfs, const char *comp, const ch TODO: add a cache for previously resolved case-insensitive names TODO: add mangled name support */ -static NTSTATUS pvfs_case_search(struct pvfs_state *pvfs, struct pvfs_filename *name) +static NTSTATUS pvfs_case_search(struct pvfs_state *pvfs, + struct pvfs_filename *name, + uint_t flags) { /* break into a series of components */ int num_components; @@ -175,7 +177,7 @@ static NTSTATUS pvfs_case_search(struct pvfs_state *pvfs, struct pvfs_filename * name->full_name = partial_name; if (name->exists) { - return pvfs_fill_dos_info(pvfs, name, -1); + return pvfs_fill_dos_info(pvfs, name, flags, -1); } return NT_STATUS_OK; @@ -515,7 +517,7 @@ NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, /* we need to search for a matching name */ saved_name = (*name)->full_name; (*name)->full_name = dir_name; - status = pvfs_case_search(pvfs, *name); + status = pvfs_case_search(pvfs, *name, flags); if (!NT_STATUS_IS_OK(status)) { /* the directory doesn't exist */ (*name)->full_name = saved_name; @@ -536,11 +538,11 @@ NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, /* if we can stat() the full name now then we are done */ if (stat((*name)->full_name, &(*name)->st) == 0) { (*name)->exists = true; - return pvfs_fill_dos_info(pvfs, *name, -1); + return pvfs_fill_dos_info(pvfs, *name, flags, -1); } /* search for a matching filename */ - status = pvfs_case_search(pvfs, *name); + status = pvfs_case_search(pvfs, *name, flags); return status; } @@ -556,7 +558,7 @@ NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, */ NTSTATUS pvfs_resolve_partial(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, const char *unix_dir, const char *fname, - struct pvfs_filename **name) + uint_t flags, struct pvfs_filename **name) { NTSTATUS status; @@ -581,7 +583,7 @@ NTSTATUS pvfs_resolve_partial(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, (*name)->stream_name = NULL; (*name)->stream_id = 0; - status = pvfs_fill_dos_info(pvfs, *name, -1); + status = pvfs_fill_dos_info(pvfs, *name, flags, -1); return status; } @@ -593,7 +595,7 @@ NTSTATUS pvfs_resolve_partial(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, to update the pvfs_filename stat information, and by pvfs_open() */ NTSTATUS pvfs_resolve_name_fd(struct pvfs_state *pvfs, int fd, - struct pvfs_filename *name) + struct pvfs_filename *name, uint_t flags) { dev_t device = (dev_t)0; ino_t inode = 0; @@ -626,7 +628,7 @@ NTSTATUS pvfs_resolve_name_fd(struct pvfs_state *pvfs, int fd, name->exists = true; - return pvfs_fill_dos_info(pvfs, name, fd); + return pvfs_fill_dos_info(pvfs, name, flags, fd); } /* @@ -703,9 +705,17 @@ NTSTATUS pvfs_resolve_name_handle(struct pvfs_state *pvfs, talloc_free(lck); } - status = pvfs_resolve_name_fd(pvfs, h->fd, h->name); + /* + * TODO: pass PVFS_RESOLVE_NO_OPENDB and get + * the write time from odb_lock() above. + */ + status = pvfs_resolve_name_fd(pvfs, h->fd, h->name, 0); NT_STATUS_NOT_OK_RETURN(status); + if (!null_nttime(h->write_time.close_time)) { + h->name->dos.write_time = h->write_time.close_time; + } + return NT_STATUS_OK; } @@ -755,7 +765,7 @@ NTSTATUS pvfs_resolve_parent(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, (*name)->stream_name = NULL; (*name)->stream_id = 0; - status = pvfs_fill_dos_info(pvfs, *name, -1); + status = pvfs_fill_dos_info(pvfs, *name, PVFS_RESOLVE_NO_OPENDB, -1); return status; } diff --git a/source4/ntvfs/posix/pvfs_search.c b/source4/ntvfs/posix/pvfs_search.c index e47406dc09..e0fe4fb64d 100644 --- a/source4/ntvfs/posix/pvfs_search.c +++ b/source4/ntvfs/posix/pvfs_search.c @@ -84,7 +84,7 @@ static NTSTATUS fill_search_info(struct pvfs_state *pvfs, in pvfs_list_seek_ofs() for how we cope with this */ - status = pvfs_resolve_partial(pvfs, file, unix_path, fname, &name); + status = pvfs_resolve_partial(pvfs, file, unix_path, fname, 0, &name); if (!NT_STATUS_IS_OK(status)) { return status; } diff --git a/source4/ntvfs/posix/pvfs_seek.c b/source4/ntvfs/posix/pvfs_seek.c index 3ea8b7cb6e..a3c4024ed7 100644 --- a/source4/ntvfs/posix/pvfs_seek.c +++ b/source4/ntvfs/posix/pvfs_seek.c @@ -52,7 +52,7 @@ NTSTATUS pvfs_seek(struct ntvfs_module_context *ntvfs, break; case SEEK_MODE_END: - status = pvfs_resolve_name_fd(pvfs, h->fd, h->name); + status = pvfs_resolve_name_fd(pvfs, h->fd, h->name, PVFS_RESOLVE_NO_OPENDB); h->seek_offset = h->name->st.st_size + io->lseek.in.offset; break; } diff --git a/source4/ntvfs/posix/pvfs_setfileinfo.c b/source4/ntvfs/posix/pvfs_setfileinfo.c index 1dd2c075d9..2cde5f42aa 100644 --- a/source4/ntvfs/posix/pvfs_setfileinfo.c +++ b/source4/ntvfs/posix/pvfs_setfileinfo.c @@ -273,7 +273,6 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs, union smb_setfileinfo *info) { struct pvfs_state *pvfs = ntvfs->private_data; - struct utimbuf unix_times; struct pvfs_file *f; struct pvfs_file_handle *h; struct pvfs_filename newstats; @@ -437,23 +436,54 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs, } /* possibly change the file timestamps */ - ZERO_STRUCT(unix_times); if (newstats.dos.create_time != h->name->dos.create_time) { change_mask |= FILE_NOTIFY_CHANGE_CREATION; } if (newstats.dos.access_time != h->name->dos.access_time) { - unix_times.actime = nt_time_to_unix(newstats.dos.access_time); change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS; } if (newstats.dos.write_time != h->name->dos.write_time) { - unix_times.modtime = nt_time_to_unix(newstats.dos.write_time); change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE; } - if (unix_times.actime != 0 || unix_times.modtime != 0) { - if (utime(h->name->full_name, &unix_times) == -1) { - return pvfs_map_errno(pvfs, errno); + if ((change_mask & FILE_NOTIFY_CHANGE_LAST_ACCESS) || + (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE)) { + struct timeval tv[2]; + + nttime_to_timeval(&tv[0], newstats.dos.access_time); + nttime_to_timeval(&tv[1], newstats.dos.write_time); + + if (!timeval_is_zero(&tv[0]) || !timeval_is_zero(&tv[1])) { + if (utimes(h->name->full_name, tv) == -1) { + DEBUG(0,("pvfs_setfileinfo: utimes() failed '%s' - %s\n", + h->name->full_name, strerror(errno))); + return pvfs_map_errno(pvfs, errno); + } } } + if (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE) { + struct odb_lock *lck; + + lck = odb_lock(req, h->pvfs->odb_context, &h->odb_locking_key); + if (lck == NULL) { + DEBUG(0,("Unable to lock opendb for write time update\n")); + return NT_STATUS_INTERNAL_ERROR; + } + + status = odb_set_write_time(lck, newstats.dos.write_time, true); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("Unable to update write time: %s\n", + nt_errstr(status))); + talloc_free(lck); + return status; + } + + talloc_free(lck); + + h->write_time.update_forced = true; + h->write_time.update_on_close = false; + talloc_free(h->write_time.update_event); + h->write_time.update_event = NULL; + } /* possibly change the attribute */ if (newstats.dos.attrib != h->name->dos.attrib) { @@ -570,7 +600,6 @@ NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs, struct pvfs_filename *name; struct pvfs_filename newstats; NTSTATUS status; - struct utimbuf unix_times; uint32_t access_needed; uint32_t change_mask = 0; struct odb_lock *lck = NULL; @@ -736,21 +765,51 @@ NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs, } /* possibly change the file timestamps */ - ZERO_STRUCT(unix_times); if (newstats.dos.create_time != name->dos.create_time) { change_mask |= FILE_NOTIFY_CHANGE_CREATION; } if (newstats.dos.access_time != name->dos.access_time) { - unix_times.actime = nt_time_to_unix(newstats.dos.access_time); change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS; } if (newstats.dos.write_time != name->dos.write_time) { - unix_times.modtime = nt_time_to_unix(newstats.dos.write_time); change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE; } - if (unix_times.actime != 0 || unix_times.modtime != 0) { - if (utime(name->full_name, &unix_times) == -1) { - return pvfs_map_errno(pvfs, errno); + if ((change_mask & FILE_NOTIFY_CHANGE_LAST_ACCESS) || + (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE)) { + struct timeval tv[2]; + + nttime_to_timeval(&tv[0], newstats.dos.access_time); + nttime_to_timeval(&tv[1], newstats.dos.write_time); + + if (!timeval_is_zero(&tv[0]) || !timeval_is_zero(&tv[1])) { + if (utimes(name->full_name, tv) == -1) { + DEBUG(0,("pvfs_setpathinfo: utimes() failed '%s' - %s\n", + name->full_name, strerror(errno))); + return pvfs_map_errno(pvfs, errno); + } + } + } + if (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE) { + if (lck == NULL) { + DATA_BLOB lkey; + status = pvfs_locking_key(name, name, &lkey); + NT_STATUS_NOT_OK_RETURN(status); + + lck = odb_lock(req, pvfs->odb_context, &lkey); + data_blob_free(&lkey); + if (lck == NULL) { + DEBUG(0,("Unable to lock opendb for write time update\n")); + return NT_STATUS_INTERNAL_ERROR; + } + } + + status = odb_set_write_time(lck, newstats.dos.write_time, true); + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { + /* it could be that nobody has opened the file */ + } else if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("Unable to update write time: %s\n", + nt_errstr(status))); + return status; } } diff --git a/source4/ntvfs/posix/pvfs_unlink.c b/source4/ntvfs/posix/pvfs_unlink.c index 4cb47a4f1f..6a57041770 100644 --- a/source4/ntvfs/posix/pvfs_unlink.c +++ b/source4/ntvfs/posix/pvfs_unlink.c @@ -201,7 +201,10 @@ NTSTATUS pvfs_unlink(struct ntvfs_module_context *ntvfs, /* resolve the cifs name to a posix name */ status = pvfs_resolve_name(pvfs, req, unl->unlink.in.pattern, - PVFS_RESOLVE_WILDCARD | PVFS_RESOLVE_STREAMS, &name); + PVFS_RESOLVE_WILDCARD | + PVFS_RESOLVE_STREAMS | + PVFS_RESOLVE_NO_OPENDB, + &name); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -246,7 +249,9 @@ NTSTATUS pvfs_unlink(struct ntvfs_module_context *ntvfs, /* get a pvfs_filename object */ status = pvfs_resolve_partial(pvfs, req, pvfs_list_unix_path(dir), - fname, &name); + fname, + PVFS_RESOLVE_NO_OPENDB, + &name); if (!NT_STATUS_IS_OK(status)) { return status; } diff --git a/source4/ntvfs/posix/pvfs_write.c b/source4/ntvfs/posix/pvfs_write.c index 1f662f13fc..2da0e4bb3a 100644 --- a/source4/ntvfs/posix/pvfs_write.c +++ b/source4/ntvfs/posix/pvfs_write.c @@ -22,7 +22,60 @@ #include "includes.h" #include "vfs_posix.h" #include "librpc/gen_ndr/security.h" +#include "lib/events/events.h" +static void pvfs_write_time_update_handler(struct event_context *ev, + struct timed_event *te, + struct timeval tv, + void *private_data) +{ + struct pvfs_file_handle *h = talloc_get_type(private_data, + struct pvfs_file_handle); + struct odb_lock *lck; + NTSTATUS status; + NTTIME write_time; + + lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key); + if (lck == NULL) { + DEBUG(0,("Unable to lock opendb for write time update\n")); + return; + } + + write_time = timeval_to_nttime(&tv); + + status = odb_set_write_time(lck, write_time, false); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("Unable to update write time: %s\n", + nt_errstr(status))); + return; + } + + talloc_free(lck); + + h->write_time.update_event = NULL; +} + +static void pvfs_trigger_write_time_update(struct pvfs_file_handle *h) +{ + struct pvfs_state *pvfs = h->pvfs; + struct timeval tv; + + if (h->write_time.update_triggered) { + return; + } + + tv = timeval_current_ofs(0, pvfs->writetime_delay); + + h->write_time.update_triggered = true; + h->write_time.update_on_close = true; + h->write_time.update_event = event_add_timed(pvfs->ntvfs->ctx->event_ctx, + h, tv, + pvfs_write_time_update_handler, + h); + if (!h->write_time.update_event) { + DEBUG(0,("Failed event_add_timed\n")); + } +} /* write to a file @@ -61,6 +114,8 @@ NTSTATUS pvfs_write(struct ntvfs_module_context *ntvfs, status = pvfs_break_level2_oplocks(f); NT_STATUS_NOT_OK_RETURN(status); + pvfs_trigger_write_time_update(f->handle); + if (f->handle->name->stream_name) { ret = pvfs_stream_write(pvfs, f->handle, diff --git a/source4/ntvfs/posix/vfs_posix.c b/source4/ntvfs/posix/vfs_posix.c index 14b5210fd0..b5dd270346 100644 --- a/source4/ntvfs/posix/vfs_posix.c +++ b/source4/ntvfs/posix/vfs_posix.c @@ -95,6 +95,10 @@ static void pvfs_setup_options(struct pvfs_state *pvfs) PVFS_OPLOCK_TIMEOUT, PVFS_OPLOCK_TIMEOUT_DEFAULT); + pvfs->writetime_delay = share_int_option(scfg, + PVFS_WRITETIME_DELAY, + PVFS_WRITETIME_DELAY_DEFAULT); + pvfs->share_name = talloc_strdup(pvfs, scfg->name); pvfs->fs_attribs = diff --git a/source4/ntvfs/posix/vfs_posix.h b/source4/ntvfs/posix/vfs_posix.h index c194698b64..cf39bcf0ac 100644 --- a/source4/ntvfs/posix/vfs_posix.h +++ b/source4/ntvfs/posix/vfs_posix.h @@ -59,6 +59,9 @@ struct pvfs_state { /* the oplock break timeout (secs) */ uint_t oplock_break_timeout; + /* the write time update delay (nsecs) */ + uint_t writetime_delay; + /* filesystem attributes (see FS_ATTR_*) */ uint32_t fs_attribs; @@ -169,6 +172,14 @@ struct pvfs_file_handle { /* we need this hook back to our parent for lock destruction */ struct pvfs_state *pvfs; + struct { + bool update_triggered; + struct timed_event *update_event; + bool update_on_close; + NTTIME close_time; + bool update_forced; + } write_time; + /* the open went through to completion */ bool open_completed; }; @@ -220,6 +231,7 @@ struct pvfs_search_state { /* flags to pvfs_resolve_name() */ #define PVFS_RESOLVE_WILDCARD (1<<0) #define PVFS_RESOLVE_STREAMS (1<<1) +#define PVFS_RESOLVE_NO_OPENDB (1<<2) /* flags in pvfs->flags */ #define PVFS_FLAG_CI_FILESYSTEM (1<<0) /* the filesystem is case insensitive */ @@ -249,6 +261,7 @@ struct pvfs_odb_retry; #define PVFS_FAKE_OPLOCKS "posix:fakeoplocks" #define PVFS_SHARE_DELAY "posix:sharedelay" #define PVFS_OPLOCK_TIMEOUT "posix:oplocktimeout" +#define PVFS_WRITETIME_DELAY "posix:writetimeupdatedelay" #define PVFS_ALLOCATION_ROUNDING "posix:allocationrounding" #define PVFS_SEARCH_INACTIVITY "posix:searchinactivity" #define PVFS_ACL "posix:acl" @@ -258,6 +271,7 @@ struct pvfs_odb_retry; #define PVFS_FAKE_OPLOCKS_DEFAULT false #define PVFS_SHARE_DELAY_DEFAULT 1000000 /* nsecs */ #define PVFS_OPLOCK_TIMEOUT_DEFAULT 30 /* secs */ +#define PVFS_WRITETIME_DELAY_DEFAULT 2000000 /* nsecs */ #define PVFS_ALLOCATION_ROUNDING_DEFAULT 512 #define PVFS_SEARCH_INACTIVITY_DEFAULT 300 diff --git a/source4/samba4-knownfail b/source4/samba4-knownfail index 67d4a4f629..ded7922ec9 100644 --- a/source4/samba4-knownfail +++ b/source4/samba4-knownfail @@ -5,7 +5,6 @@ # a successful run for any of these tests an error. local.resolve.*.async local.iconv.*.next_codepoint() -base.delaywrite.finfo update on close base.delete.*.deltest20a base.delete.*.deltest20b rpc.winreg.*security diff --git a/source4/samba4-skip b/source4/samba4-skip index 7a6c54e9ec..bf25172c76 100644 --- a/source4/samba4-skip +++ b/source4/samba4-skip @@ -14,7 +14,6 @@ # # Please add a comment for each testsuite you disable explaining why # it is being skipped. -base.delaywrite raw.composite base.iometer base.casetable @@ -28,7 +27,6 @@ samba4.ntvfs.cifs.raw.qfileinfo.ipc smb2.notify smb2.scan ntvfs.cifs.base.charset -ntvfs.cifs.base.delaywrite ntvfs.cifs.base.iometer ntvfs.cifs.base.casetable ntvfs.cifs.base.nttrans diff --git a/source4/selftest/samba4_tests.sh b/source4/selftest/samba4_tests.sh index b73ba55586..1d550cbfe1 100755 --- a/source4/selftest/samba4_tests.sh +++ b/source4/selftest/samba4_tests.sh @@ -213,7 +213,11 @@ done plantest "rpc.echo on ncacn_np over smb2" dc $smb4torture ncacn_np:"\$SERVER[smb2]" -U"\$USERNAME"%"\$PASSWORD" -W \$DOMAIN RPC-ECHO "$*" # Tests against the NTVFS POSIX backend -NTVFSARGS="--option=torture:sharedelay=100000 --option=torture:oplocktimeout=3" +NTVFSARGS="" +NTVFSARGS="${NTVFSARGS} --option=torture:sharedelay=100000" +NTVFSARGS="${NTVFSARGS} --option=torture:oplocktimeout=3" +NTVFSARGS="${NTVFSARGS} --option=torture:writetimeupdatedelay=500000" + smb2=`$smb4torture --list | grep "^SMB2-" | xargs` #The QFILEINFO-IPC test needs to be on ipc$ raw=`$smb4torture --list | grep "^RAW-" | grep -v "RAW-QFILEINFO-IPC"| xargs` diff --git a/source4/selftest/target/Samba4.pm b/source4/selftest/target/Samba4.pm index 9f771ab8a3..bcee926481 100644 --- a/source4/selftest/target/Samba4.pm +++ b/source4/selftest/target/Samba4.pm @@ -581,6 +581,7 @@ sub provision($$$$$$) posix:sharedelay = 100000 posix:eadb = $lockdir/eadb.tdb posix:oplocktimeout = 3 + posix:writetimeupdatedelay = 500000 [test1] path = $tmpdir/test1 @@ -589,6 +590,7 @@ sub provision($$$$$$) posix:sharedelay = 100000 posix:eadb = $lockdir/eadb.tdb posix:oplocktimeout = 3 + posix:writetimeupdatedelay = 500000 [test2] path = $tmpdir/test2 @@ -597,6 +599,7 @@ sub provision($$$$$$) posix:sharedelay = 100000 posix:eadb = $lockdir/eadb.tdb posix:oplocktimeout = 3 + posix:writetimeupdatedelay = 500000 [cifs] read only = no diff --git a/source4/torture/basic/delaywrite.c b/source4/torture/basic/delaywrite.c index ac4f565a2b..c03e89d36e 100644 --- a/source4/torture/basic/delaywrite.c +++ b/source4/torture/basic/delaywrite.c @@ -641,7 +641,7 @@ static bool test_finfo_after_write(struct torture_context *tctx, struct smbcli_s bool err = false; \ if (strict && (g cmp c)) { \ err = true; \ - } else if (gr cmp cr) { \ + } else if ((g cmp c) && (gr cmp cr)) { \ /* handle filesystem without high resolution timestamps */ \ err = true; \ } \ @@ -816,6 +816,7 @@ static bool test_delayed_write_update3(struct torture_context *tctx, } GET_INFO_BOTH(finfo1,pinfo1); + COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0); /* sure any further write doesn't update the write time */ start = timeval_current(); @@ -970,6 +971,7 @@ static bool test_delayed_write_update4(struct torture_context *tctx, } GET_INFO_BOTH(finfo1,pinfo1); + COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0); /* sure any further write doesn't update the write time */ start = timeval_current(); diff --git a/testprogs/ejs/ldap.js b/testprogs/ejs/ldap.js index 44e4c83e67..a5e6ab37c4 100755 --- a/testprogs/ejs/ldap.js +++ b/testprogs/ejs/ldap.js @@ -486,11 +486,11 @@ sn: ldap user2 assert(res.msgs.length == 2); } - var res = ldb.search("(&(anr=testy ldap)(objectClass=user))"); - if (res.error != 0 || res.msgs.length != 2) { - println("Found only " + res.msgs.length + " for (&(anr=\"testy ldap\")(objectClass=user))"); + var res = ldb.search("(&(anr=\"testy ldap\")(objectClass=user))"); + if (res.error != 0 || res.msgs.length != 0) { + println("Found " + res.msgs.length + " for (&(anr=\"testy ldap\")(objectClass=user))"); assert(res.error == 0); - assert(res.msgs.length == 2); + assert(res.msgs.length == 0); } // Testing ldb.search for (&(anr=ldap)(objectClass=user)) |