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:
Ken Dreyer 2020-07-24 14:59:41 -06:00 committed by Tomas Kopecek
parent 1aa4fb5185
commit e35b571196
2 changed files with 26 additions and 1 deletions

View file

@ -23,6 +23,7 @@ from __future__ import absolute_import, division
import base64
import calendar
import datetime
import errno
import hashlib
import logging
import os
@ -472,7 +473,12 @@ def _stripcwd(dev):
"""Unlink all files in cwd and return list of subdirs"""
dirs = []
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:
# don't cross fs boundary
continue

View file

@ -1,6 +1,7 @@
# coding=utf-8
from __future__ import absolute_import
import calendar
import errno
import locale
import mock
import optparse
@ -1424,6 +1425,24 @@ class TestRmtree(unittest.TestCase):
isdir.assert_has_calls([call('mode'), call('mode')])
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):
@mock.patch('koji.ensuredir')
@mock.patch('koji.util.safer_move')