Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

renameFile now consistently reports an error if the destination is a directory #8

Merged
merged 1 commit into from
Mar 4, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 23 additions & 16 deletions System/Directory.hs
Original file line number Diff line number Diff line change
Expand Up @@ -638,24 +638,31 @@ Either path refers to an existing directory.

renameFile :: FilePath -> FilePath -> IO ()
renameFile opath npath = do
-- XXX this test isn't performed atomically with the following rename
#ifdef mingw32_HOST_OS
-- ToDo: use Win32 API
withFileOrSymlinkStatus "renameFile" opath $ \st -> do
is_dir <- isDirectory st
#else
stat <- Posix.getSymbolicLinkStatus opath
let is_dir = Posix.isDirectory stat
#endif
if is_dir
then ioError (ioeSetErrorString
(mkIOError InappropriateType "renameFile" Nothing (Just opath))
"is a directory")
else do
-- XXX the isDirectory tests are not performed atomically with the rename
checkNotDir opath
doRename `E.catch` renameExcHandler
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, unless the problem manifests itself elsewhere, renameExcHandler should be omitted for non-Windows systems.

where checkNotDir path = do
isdir <- pathIsDir path `E.catch` ((\ _ -> return False) :: IOException -> IO Bool)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use catchIOError to avoid specifying the type signature.

On second thought, I'm not sure if catchIOError is available in older versions of GHC. It is for 7.4 at least.

when isdir $ dirIoError path
dirIoError path = ioError $ ioeSetErrorString (mkIOError InappropriateType "renameFile" Nothing (Just path)) "is a directory"
renameExcHandler :: IOException -> IO ()
renameExcHandler exc = do
-- The underlying rename implementation throws odd exceptions
-- sometimes when the destination is a directory. For example,
-- Windows throws a permission error. In those cases check
-- if the cause is actually the destination being a directory
-- and throw InapprioriateType in that case.
checkNotDir npath
throw exc
doRename :: IO ()
pathIsDir :: FilePath -> IO (Bool)
#ifdef mingw32_HOST_OS
Win32.moveFileEx opath npath Win32.mOVEFILE_REPLACE_EXISTING
-- ToDo: use Win32 API
pathIsDir path = withFileOrSymlinkStatus "renameFile" path isDirectory
doRename = Win32.moveFileEx opath npath Win32.mOVEFILE_REPLACE_EXISTING
#else
Posix.rename opath npath
pathIsDir path = Posix.isDirectory `fmap` Posix.getSymbolicLinkStatus path
doRename = Posix.rename opath npath
#endif

#endif /* __GLASGOW_HASKELL__ */
Expand Down
16 changes: 16 additions & 0 deletions tests/T8482.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import System.Directory
import Control.Exception

tmp1 = "T8482.tmp1"
testdir = "T8482.dir"

main = do
writeFile tmp1 "hello"
createDirectory testdir
tryRenameFile testdir tmp1 >>= print -- InappropriateType
tryRenameFile tmp1 testdir >>= print -- InappropriateType
tryRenameFile tmp1 "." >>= print -- InappropriateType
removeDirectory testdir
removeFile tmp1
where tryRenameFile :: FilePath -> FilePath -> IO (Either IOException ())
tryRenameFile opath npath = try $ renameFile opath npath
3 changes: 3 additions & 0 deletions tests/T8482.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Left T8482.dir: renameFile: inappropriate type (is a directory)
Left T8482.dir: renameFile: inappropriate type (is a directory)
Left .: renameFile: inappropriate type (is a directory)
1 change: 1 addition & 0 deletions tests/all.T
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ test('createDirectoryIfMissing001', normal, compile_and_run, [''])
test('getHomeDirectory001', ignore_output, compile_and_run, [''])

test('T4113', when(platform('i386-apple-darwin'), expect_broken(7604)), compile_and_run, [''])
test('T8482', normal, compile_and_run, [''])