0

The file-writable-p docstring says:

Return t if file FILENAME can be written or created by you.

In Windows you cannot usually write on files when they are used by a different app. In these instances you get a permission denied error since the process blocks the access.

For example, in most of the viewers, if you open ~/foo.pdf, you cannot write access to it. However:

(file-writable-p "~/foo.pdf")

still returns t. Therefore, with ~/foo.pdf open,

(if (file-writable-p "~/foo.pdf")
    (with-temp-file "~/foo.pdf" (insert "Hello World")))

returns the error: (file-error "Opening output file" "Permission denied" ..... The same happens with:

(if (file-writable-p "~/foo.pdf")
    (delete-file "~/foo.pdf"))

Is this the way file-writable-p is intended to work?
If so, which is the proper path to safely write some output to a file?

  • This doesn't sound like an Emacs question. Check whether you have execute (x) permissions on the directory where the file is. You typically need that. – Drew Jan 29 '18 at 18:29
  • "Blocked by another application" sounds like a Windows issue. The best way to handle these is to just error out and not attempt doing anything with that file. – wasamasa Jan 29 '18 at 18:46
  • @wasamasa: "Sounds like a Windows issue": of course it is (see the title). "error out and not attempt doing anything with that file": this is what I would like to do with file-writable-p or an equivalent Emacs file system function. – antonio Feb 1 '18 at 8:35
  • 1
    It's not a matter of file permissions, it's your operating system locking files when accessed by an application (so that access by another application fails). There's nothing you can do about it, best leave the error unhandled or if you must, offer the user to retry later. – wasamasa Feb 1 '18 at 8:48
  • 2
    Furthermore, this pattern is considered bad enough to be a vulnerability class of its own: en.wikipedia.org/wiki/Time_of_check_to_time_of_use – wasamasa Feb 1 '18 at 14:27
0

A version of file-writable-p which returns nil if a Windows process is blocking write access.

(defun file-unblocked-p (filename)
  "This function works similarly to `file-writable-p' but is  with Windows permission denied errors. Therefore it returns nil if another process is blocking write access."  

  (if (not (file-exists-p filename))
      (file-writable-p filename)
    (not (condition-case err
             (rename-file filename filename)        
           (error err)))))

Note: I have tested it on Windows only.

Use this function wisely. In the generality of cases:

(if (file-unblocked-p "~/foo.pdf")
    (delete-file "~/foo.pdf"))

should work, but it is always possible that the writable condition holds when you test it and does not when you actually delete (write) the file. It could be pretty rare, but that depends on your specific use case. If one wants to be absolutely sure to avoid race conditions, one could just write and then check for errors:

(not (condition-case err
     (delete-file filename)        
       (error err)))
  • The docstring for rename-file says it signals a file-already-exists error if the new filename already exists, so file-writable-cross-p returns nil for unlocked and writable files. Would the optional argument OK-IF-ALREADY-EXISTS help? By the way, if you think the behaviour of file-writable-p is incorrect you should report it with M-x report-emacs-bug RET. – Basil Feb 1 '18 at 13:23
  • @Basil: Have you tried this in Linux? In Windows, the problem happens only, when the directory is different: (rename-file "~/foo.pdf" "~/sub/foo.pdf"), which is never the case here. More than a bug, it is a misnomer. The function addresses Linux style permissions. Still a user might think that, if they can write to a file, no process is blocking it preventing them to do so. To avoid confusion I have changed my name. – antonio Feb 1 '18 at 16:16
  • Yes, I'm on GNU/Linux, and I understand your description of the underlying issue, but what your function says it does is different from what it actually does, since it returns nil even for writable files which are not being blocked by any other process. Furthermore, the docstring of rename-file should be correct for all platforms. If its behaviour does not match its docstring please consider reporting it. – Basil Feb 1 '18 at 16:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.