file - allow touch on files not owned by user (#50964)

* file - allow touch on files not owned by user

* use Sentinal value and preserved existing args

* Do no instantiate the Sentinel object
This commit is contained in:
Jordan Borean 2019-01-23 10:14:59 +10:00 committed by GitHub
parent 8fd0fbe431
commit 419727a6da
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 71 additions and 16 deletions

View file

@ -0,0 +1,2 @@
bugfixes:
- file - Allow state=touch on file the user does not own https://github.com/ansible/ansible/issues/50943

View file

@ -211,6 +211,11 @@ class ParameterError(AnsibleModuleError):
pass
class Sentinel(object):
def __new__(cls, *args, **kwargs):
return cls
def _ansible_excepthook(exc_type, exc_value, tb):
# Using an exception allows us to catch it if the calling code knows it can recover
if issubclass(exc_type, AnsibleModuleError):
@ -349,8 +354,7 @@ def get_timestamp_for_time(formatted_time, time_format):
if formatted_time == 'preserve':
return None
elif formatted_time == 'now':
current_time = time.time()
return current_time
return Sentinel
else:
try:
struct = time.strptime(formatted_time, time_format)
@ -363,25 +367,43 @@ def get_timestamp_for_time(formatted_time, time_format):
def update_timestamp_for_file(path, mtime, atime, diff=None):
# If both parameters are None, nothing to do
if mtime is None and atime is None:
return False
try:
previous_mtime = os.stat(path).st_mtime
previous_atime = os.stat(path).st_atime
# When mtime and atime are set to 'now', rely on utime(path, None) which does not require ownership of the file
# https://github.com/ansible/ansible/issues/50943
if mtime is Sentinel and atime is Sentinel:
# It's not exact but we can't rely on os.stat(path).st_mtime after setting os.utime(path, None) as it may
# not be updated. Just use the current time for the diff values
mtime = atime = time.time()
if mtime is None:
mtime = previous_mtime
previous_mtime = os.stat(path).st_mtime
previous_atime = os.stat(path).st_atime
if atime is None:
atime = previous_atime
set_time = None
else:
# If both parameters are None 'preserve', nothing to do
if mtime is None and atime is None:
return False
# If both timestamps are already ok, nothing to do
if mtime == previous_mtime and atime == previous_atime:
return False
previous_mtime = os.stat(path).st_mtime
previous_atime = os.stat(path).st_atime
os.utime(path, (atime, mtime))
if mtime is None:
mtime = previous_mtime
elif mtime is Sentinel:
mtime = time.time()
if atime is None:
atime = previous_atime
elif atime is Sentinel:
atime = time.time()
# If both timestamps are already ok, nothing to do
if mtime == previous_mtime and atime == previous_atime:
return False
set_time = (atime, mtime)
os.utime(path, set_time)
if diff is not None:
if 'before' not in diff:

View file

@ -488,6 +488,37 @@
that:
- result.mode == '0444'
# https://github.com/ansible/ansible/issues/50943
# Need to use /tmp as nobody can't access output_dir at all
- name: create file as root with all write permissions
file: dest=/tmp/write_utime state=touch mode=0666 owner={{ansible_user}}
- block:
- name: get previous time
stat: path=/tmp/write_utime
register: previous_time
- name: touch file as nobody
file: dest=/tmp/write_utime state=touch
become: True
become_user: nobody
register: result
- name: get new time
stat: path=/tmp/write_utime
register: current_time
always:
- name: remove test utime file
file: path=/tmp/write_utime state=absent
- name: assert touch file as nobody
assert:
that:
- result is changed
- current_time.stat.atime > previous_time.stat.atime
- current_time.stat.mtime > previous_time.stat.mtime
# Follow + recursive tests
- name: create a toplevel directory
file: path={{output_dir}}/test_follow_rec state=directory mode=0755