Skip to content

Saving a writable file in a non-writable directory is not possible #1515

Closed
@krassowski

Description

@krassowski

Description

Saving a writable file in a non-writable directory is not possible

Reproduce

  1. mkdir non-writable
  2. touch non-writable/writable.txt
  3. chmod 500 non-writable
  4. chmod 777 non-writable/writable.txt
  5. jupyter lab
  6. Open non-writable/writable.txt, type something, save
  7. See error in UI:
File Save Error for writable.txt
Permission denied: non-writable/writable.txt
  1. See error in terminal:
Traceback (most recent call last):
  File "/jupyter_server/services/contents/fileio.py", line 230, in perm_to_403
    yield
  File "/jupyter_server/services/contents/fileio.py", line 220, in atomic_writing
    with atomic_writing(os_path, *args, **kwargs) as f:
  File "/contextlib.py", line 137, in __enter__
    return next(self.gen)
           ^^^^^^^^^^^^^^
  File "/jupyter_server/services/contents/fileio.py", line 107, in atomic_writing
    copy2_safe(path, tmp_path, log=log)
  File "/jupyter_server/services/contents/fileio.py", line 42, in copy2_safe
    shutil.copyfile(src, dst)
  File "/shutil.py", line 258, in copyfile
    with open(dst, 'wb') as fdst:
         ^^^^^^^^^^^^^^^
PermissionError: [Errno 13] Permission denied: '/non-writable/.~writable.txt'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/site-packages/tornado/web.py", line 1790, in _execute
    result = await result
             ^^^^^^^^^^^^
  File "/jupyter_server/auth/decorator.py", line 73, in inner
    return await out
           ^^^^^^^^^
  File "/jupyter_server/services/contents/handlers.py", line 320, in put
    await self._save(model, path)
  File "/jupyter_server/services/contents/handlers.py", line 245, in _save
    model = await ensure_async(self.contents_manager.save(model, path))
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/site-packages/jupyter_core/utils/__init__.py", line 198, in ensure_async
    result = await obj
             ^^^^^^^^^
  File "/jupyter_server/services/contents/largefilemanager.py", line 133, in save
    return await super().save(model, path)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/jupyter_server/services/contents/filemanager.py", line 969, in save
    await self._save_file(os_path, model["content"], model.get("format"))
  File "/jupyter_server/services/contents/fileio.py", line 572, in _save_file
    with self.atomic_writing(os_path, text=False) as f:
  File "/contextlib.py", line 137, in __enter__
    return next(self.gen)
           ^^^^^^^^^^^^^^
  File "/jupyter_server/services/contents/fileio.py", line 217, in atomic_writing
    with self.perm_to_403(os_path):
  File "/contextlib.py", line 158, in __exit__
    self.gen.throw(typ, value, traceback)
  File "/jupyter_server/services/contents/fileio.py", line 239, in perm_to_403
    raise HTTPError(403, "Permission denied: %s" % path) from e
tornado.web.HTTPError: HTTP 403: Forbidden (Permission denied: non-writable/writable.txt)

Expected behavior

No error, file can be saved.

Maybe:

  • skip atomic write in this case?
  • still use atomic write but create the temporary file in a temporary directory instead if that is writable?

Context

This is very similar to #1502

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions