util: handle ENOENT in _stripcwd() listdir loop
After we call listdir(), another thread or process might delete files from the current directory. We cannot always expect lstat() to succeed in a loop over the initial listdir entries. If lstat() raises ENOENT, consider this file deleted and move on.
This commit is contained in:
parent
1aa4fb5185
commit
e35b571196
2 changed files with 26 additions and 1 deletions
|
|
@ -23,6 +23,7 @@ from __future__ import absolute_import, division
|
||||||
import base64
|
import base64
|
||||||
import calendar
|
import calendar
|
||||||
import datetime
|
import datetime
|
||||||
|
import errno
|
||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
@ -472,7 +473,12 @@ def _stripcwd(dev):
|
||||||
"""Unlink all files in cwd and return list of subdirs"""
|
"""Unlink all files in cwd and return list of subdirs"""
|
||||||
dirs = []
|
dirs = []
|
||||||
for fn in os.listdir('.'):
|
for fn in os.listdir('.'):
|
||||||
st = os.lstat(fn)
|
try:
|
||||||
|
st = os.lstat(fn)
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno == errno.ENOENT:
|
||||||
|
continue
|
||||||
|
raise
|
||||||
if st.st_dev != dev:
|
if st.st_dev != dev:
|
||||||
# don't cross fs boundary
|
# don't cross fs boundary
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
import calendar
|
import calendar
|
||||||
|
import errno
|
||||||
import locale
|
import locale
|
||||||
import mock
|
import mock
|
||||||
import optparse
|
import optparse
|
||||||
|
|
@ -1424,6 +1425,24 @@ class TestRmtree(unittest.TestCase):
|
||||||
isdir.assert_has_calls([call('mode'), call('mode')])
|
isdir.assert_has_calls([call('mode'), call('mode')])
|
||||||
lstat.assert_has_calls([call('a'), call('b')])
|
lstat.assert_has_calls([call('a'), call('b')])
|
||||||
|
|
||||||
|
@patch('os.listdir')
|
||||||
|
@patch('os.lstat')
|
||||||
|
@patch('stat.S_ISDIR')
|
||||||
|
@patch('os.unlink')
|
||||||
|
def test_stripcwd_stat_fail(dev, unlink, isdir, lstat, listdir):
|
||||||
|
# something else deletes a file in the middle of _stripcwd()
|
||||||
|
dev = 'dev'
|
||||||
|
listdir.return_value = ['will-not-exist.txt']
|
||||||
|
lstat.side_effect = OSError(errno.ENOENT, 'No such file or directory')
|
||||||
|
|
||||||
|
koji.util._stripcwd(dev)
|
||||||
|
|
||||||
|
listdir.assert_called_once_with('.')
|
||||||
|
lstat.assert_called_once_with('will-not-exist.txt')
|
||||||
|
unlink.assert_not_called()
|
||||||
|
isdir.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
class TestMoveAndSymlink(unittest.TestCase):
|
class TestMoveAndSymlink(unittest.TestCase):
|
||||||
@mock.patch('koji.ensuredir')
|
@mock.patch('koji.ensuredir')
|
||||||
@mock.patch('koji.util.safer_move')
|
@mock.patch('koji.util.safer_move')
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue