diff options
author | Andrew Tridgell <tridge@samba.org> | 2008-06-03 15:11:42 -0700 |
---|---|---|
committer | Andrew Tridgell <tridge@samba.org> | 2008-06-03 15:11:42 -0700 |
commit | 70219bee139445ab6116def04e25c245871f8391 (patch) | |
tree | 5bd69a8fe4e664ca5abfeaed1ce2983ebb5f48ca /source4 | |
parent | 7c926ff1150133127c73b9b46d82524f57b3c616 (diff) | |
parent | 446748bfe1f06d0900e44fe8acafdf856d54486e (diff) | |
download | samba-70219bee139445ab6116def04e25c245871f8391.tar.gz samba-70219bee139445ab6116def04e25c245871f8391.tar.bz2 samba-70219bee139445ab6116def04e25c245871f8391.zip |
Merge branch 'v4-0-test' of ssh://git.samba.org/data/git/samba into v4-0-test
(This used to be commit 9d74201e462f2f95ed5bd91200681db3bd2a9277)
Diffstat (limited to 'source4')
24 files changed, 544 insertions, 147 deletions
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(); |