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

PermissionError: [Errno 13] Permission denied: '/tmp/backintime.lock' #1676

Closed
khaeusler opened this issue Apr 5, 2024 · 14 comments · Fixed by #1697
Closed

PermissionError: [Errno 13] Permission denied: '/tmp/backintime.lock' #1676

khaeusler opened this issue Apr 5, 2024 · 14 comments · Fixed by #1697
Assignees
Labels
Bug Discussion decision or consensus needed Reproduced

Comments

@khaeusler
Copy link

Hello,

I have a problem with the lock file.
First the backup of my user is executed and then always the backup of the root account.

The backup of the root user is not executed because the lock file '/tmp/backintime.lock' exists.

If I then start backintime via the console, I get the following message on the console when I click the backup button:

$ sudo backintime-qt_polkit
[sudo] password for XXX:

Back In Time
Version: 1.4.3

Back In Time comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
under certain conditions; type `backintime --license' for details.

QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '/tmp/runtime-root'

Back In Time
Version: 1.4.3

Back In Time comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
under certain conditions; type `backintime --license' for details.

Traceback (most recent call last):
File "/usr/share/backintime/common/backintime.py", line 1190, in <module>
startApp()
File "/usr/share/backintime/common/backintime.py", line 523, in startApp
args.func(args)
File "/usr/share/backintime/common/backintime.py", line 764, in backup
ret = takeSnapshot(cfg, force)
File "/usr/share/backintime/common/backintime.py", line 97, in takeSnapshot
ret = snapshots.Snapshots(cfg).backup(force)
File "/usr/share/backintime/common/snapshots.py", line 732, in backup
self.flockExclusive()  # global flock to block backups from other profiles or users (and run them serialized)
File "/usr/share/backintime/common/snapshots.py", line 2028, in flockExclusive
self.flock = open(self.GLOBAL_FLOCK, 'w')
PermissionError: [Errno 13] Permission denied: '/tmp/backintime.lock'
$ ls -l /tmp/backintime.lock
-rw-rw-rw- 1 XXX XXX 0 Apr  5 12:10 /tmp/backintime.lock

After removing the lock file, the backup runs without errors. The lock file is then created by Backintime as follows:

$ ls -l /tmp/backintime.lock
-rw-rw-rw- 1 root root 0 Apr  5 13:16 /tmp/backintime.lock

However, if the root lock file exists, there are no problems when the normal user starts a backup.

backintime --diagnostics output
$ sudo backintime --diagnostics
[sudo] password for XXX: 
QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '/tmp/runtime-root'
QSettings::value: Empty key passed
QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '/tmp/runtime-root'
QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '/tmp/runtime-root'
QSettings::value: Empty key passed
{
    "backintime": {
        "name": "Back In Time",
        "version": "1.4.3",
        "latest-config-version": 6,
        "local-config-file": "/root/.config/backintime/config",
        "local-config-file-found": true,
        "global-config-file": "/etc/backintime/config",
        "global-config-file-found": false,
        "started-from": "/usr/share/backintime/common",
        "running-as-root": true,
        "user-callback": "/root/.config/backintime/user-callback",
        "keyring-supported": false
    },
    "host-setup": {
        "platform": "Linux-6.1.0-1036-oem-x86_64-with-glibc2.35",
        "system": "Linux #36-Ubuntu SMP PREEMPT_DYNAMIC Mon Mar 11 17:32:20 UTC 2024",
        "OS": {
            "/etc/os-release": "Ubuntu 22.04.4 LTS",
            "/etc/lsb-release": "DISTRIB_ID=Ubuntu\nDISTRIB_RELEASE=22.04\nDISTRIB_CODENAME=jammy\nDISTRIB_DESCRIPTION=\"Ubuntu 22.04.4 LTS\"\n",
            "/etc/debian_version": "bookworm/sid\n"
        },
        "display-system": "($XDG_SESSION_TYPE not set)",
        "locale": "de_DE, UTF-8",
        "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin",
        "RSYNC_OLD_ARGS": "(not set)",
        "RSYNC_PROTECT_ARGS": "(not set)"
    },
    "python-setup": {
        "python": "3.10.12 main Nov 20 2023 15:14:05 CPython GCC 11.4.0",
        "python-executable": "/usr/bin/python3",
        "python-executable-symlink": true,
        "python-executable-resolved": "/usr/bin/python3.10",
        "sys.path": [
            "/usr/share/backintime/qt/plugins",
            "/usr/share/backintime/common/plugins",
            "/usr/share/backintime/plugins",
            "/usr/share/backintime/common",
            "/usr/lib/python310.zip",
            "/usr/lib/python3.10",
            "/usr/lib/python3.10/lib-dynload",
            "/usr/local/lib/python3.10/dist-packages",
            "/usr/lib/python3/dist-packages"
        ],
        "qt": {
            "Version": "PyQt 5.15.6 / Qt 5.15.3",
            "Theme": "breeze",
            "Theme Search Paths": [
                "/usr/share/icons",
                ":/icons"
            ],
            "Fallback Theme": "breeze",
            "Fallback Search Paths": []
        }
    },
    "external-programs": {
        "rsync": {
            "version": "3.2.7",
            "protocol": "31.0",
            "capabilities": {
                "file_bits": 64,
                "inum_bits": 64,
                "timestamp_bits": 64,
                "long_int_bits": 64,
                "socketpairs": true,
                "symlinks": true,
                "symtimes": true,
                "hardlinks": true,
                "hardlink_specials": true,
                "hardlink_symlinks": true,
                "IPv6": true,
                "atimes": true,
                "batchfiles": true,
                "inplace": true,
                "append": true,
                "ACLs": true,
                "xattrs": true,
                "secluded_args": "optional",
                "iconv": true,
                "prealloc": true,
                "stop_at": true,
                "crtimes": false
            },
            "optimizations": {
                "SIMD_roll": true,
                "asm_roll": false,
                "openssl_crypto": true,
                "asm_MD5": false
            },
            "checksum_list": [
                "xxh128",
                "xxh3",
                "xxh64",
                "md5",
                "md4",
                "sha1",
                "none"
            ],
            "compress_list": [
                "zstd",
                "lz4",
                "zlibx",
                "zlib",
                "none"
            ],
            "daemon_auth_list": [
                "sha512",
                "sha256",
                "sha1",
                "md5",
                "md4"
            ]
        },
        "ssh": "OpenSSH_8.9p1 Ubuntu-3ubuntu0.6, OpenSSL 3.0.2 15 Mar 2022",
        "sshfs": "3.7.1",
        "encfs": "1.9.5",
        "shell": "/bin/bash",
        "shell-version": "GNU bash, version 5.1.16(1)-release (x86_64-pc-linux-gnu)"
    }
}
@buhtz
Copy link
Member

buhtz commented Apr 5, 2024

Hello Kay,
thanks for taking the time to report this.

Questions

Execution

Can you explain in more details how the two backup jobs (the users's and root's one) are "executed"? Manually via GUI, via commandline or schedule via crontab?

For example I wonder about this.

$ sudo backintime-qt_polkit

No need to use sudo here. Just backintime-qt_polkit as regular user. But I am not sure if this will make a difference regarding to your problem.

Global lock settings

Can you have a look (as root & user) in the Manage profiles dialog in the Options tab and see if the option "Run only one snapshot at a time" is set?

Reproduced

It seems I can reproduce the problem (on Debian 12 with BIT 1.4.4-dev).

  1. Setup two snapshots profiles, one as user and one as root.
  2. Enable the "Run only one snapshot at a time" option in Options TAB of the Manage profiles dialog.
  3. As user take a snapshot and close BIT after it. The file /tmp/backintime.lock is created (-rw-rw-rw- 1 user user) and not deleted when BIT is closed.
  4. As root take a snapshot (using --debug switch) and you will get the PermissionError problem.

This not happens when root runs first and user as second snapshot.

To my team mates

[removed because irrelevant]

@buhtz buhtz added Reproduced Discussion decision or consensus needed Bug labels Apr 5, 2024
@buhtz buhtz added this to the Upcoming release (1.5.0) milestone Apr 5, 2024
@khaeusler
Copy link
Author

khaeusler commented Apr 5, 2024

Hello Christian,

BIT starts via udev

ACTION=="add|change", ENV{ID_FS_UUID}=="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", RUN+="/usr/bin/su - 'XXX' -c '/usr/bin/nice -n19 /usr/bin/ionice -c2 -n7 /usr/bin/backintime backup-job >/dev/null'"
ACTION=="add|change", ENV{ID_FS_UUID}=="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", RUN+="/usr/bin/su - 'root' -c '/usr/bin/nice -n19 /usr/bin/ionice -c2 -n7 /usr/bin/backintime --profile-id 2 backup-job >/dev/null'"

@buhtz
Copy link
Member

buhtz commented Apr 5, 2024

Have you configured this yourself or is this generated by BIT? I ask because I have not written that part of BIT and I don't use udev.
Root and the user do use the same ID_FS_UUID as backup destination?

@khaeusler
Copy link
Author

The udev roles were created directly via BIT; yes the same external HDD is used.

@khaeusler
Copy link
Author

Global lock settings

Can you have a look (as root & user) in the Manage profiles dialog in the Options tab and see if the option "Run only one snapshot at a time" is set?

Yes, this option is set for both profiles.

@buhtz
Copy link
Member

buhtz commented Apr 5, 2024

Am I correct in assuming that the files/folders in source and destination of the two snapshot profiles don't actually conflict with each other, but rather you want to reduce the load on the one external hard drive, so you intend for both jobs to run one after the other?

Why not opening for just reading (r) instead of writing (w)?

self.flock = open(self.GLOBAL_FLOCK, 'w')

Regarding to my quick n dirty tests this might solve the problem. But let's wait on my team mates with some more expertise in this than me. 😃

Note to me (hint from #python@libera.chat): Make sure the file exist or is created when using read mode.

@khaeusler
Copy link
Author

Am I correct in assuming that the files/folders in source and destination of the two snapshot profiles don't actually conflict with each other, but rather you want to reduce the load on the one external hard drive, so you intend for both jobs to run one after the other?

Yes, to reduce the load. If both are running in parallel, it can happen that the load goes over 20, e.g. if I'm still on YouTube, it can jerk.

@gitraphha
Copy link

Why not opening for just reading (r) instead of writing (w)?

self.flock = open(self.GLOBAL_FLOCK, 'w')

Might work, didn't test - still does not explain (for me), why a regular user can run their backup job while root cannot.
see #1122 (comment)

@buhtz
Copy link
Member

buhtz commented Apr 17, 2024

Beside change the open mode from write to read another proposal could be to relocate the flock file itself.

GLOBAL_FLOCK = '/tmp/backintime.lock'

Can someone of you test it to modify that path to /run/lock/backintime.lock if the folder exist, otherwise /var/lock/backintime.lock?

My proposal

  • Move the lock file into /run/lock or /var/lock if the first does not exist. These are the recommended default locations for flock files. To my knowledge and understanding the Linux kernel's security feature is restricted to files in /tmp only.
  • Keep the write mode because this avoid to add an extra if-exist-check.
  • Realize the flock handling with a context manager. It is more pythonic with clean and isolated code.
  • Have a look at common/applicationinstance.py::ApplicationInstance.flockExclusive() and take it into account when developing the context manager. But keep that code and work on it in a separate PR. It could be said that the class ApplicationInstance is kind of an primitive context manager.
  • Update the flock docu in doc-dev if necessary.

@buhtz
Copy link
Member

buhtz commented Apr 21, 2024

I opened PR #1697. Would be glad if you could give it a try and test it in your environment. Let me know if you have further questions or if I can be of assistance.

@khaeusler
Copy link
Author

After I copied flock.py and snapshots.py to /usr/share/backintime/common and connected the HDD, there were no more problems with the root user's backup process.

@aryoda
Copy link
Contributor

aryoda commented May 3, 2024

@buhtz

3. As user take a snapshot and close BIT after it. The file /tmp/backintime.lock is created (-rw-rw-rw- 1 user user) and not deleted when BIT is closed.

Great work! This looks like a bug in BiT anyhow (= not releasing a lock file) and introducing a context manager instead of using our legacy ApplicationInstance seems to fix this as you mentioned in your PR comment (https://github.com/bit-team/backintime/pull/1697/files#r1573930887)

I think I need to update my locking documentation once this fix is merged...

Furthermore I am wondering if the missing access rights for root may be the reason for #1592 too because X11 requires access to a "temp" folder too and we "borrow" the folder from the user when BiT is running as "root"...
I will check this once I find the time to continue working on that issue...

@buhtz
Copy link
Member

buhtz commented May 3, 2024

Furthermore I am wondering if the missing access rights for root may be the reason for #1592 too because X11 requires access to a "temp" folder too and we "borrow" the folder from the user when BiT is running as "root"... I will check this once I find the time to continue working on that issue...

Might be the case. I couldn't find the document explaining (in human readable form) this Kernel security feature. I just found this commit message: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=30aba6656f61ed44cba445a3c0d38b296fa9e8f5

To my understanding this feature was introduced not long ago in the Linux kernel. It seems to be an attack vector if root writes to tmp-files that are not owned by root. So it is prohibited not matter how the "others" or "groups" permissions on such a file might be. Root is not allowed to write to files in /tmp not owned by root.

@buhtz
Copy link
Member

buhtz commented Jun 2, 2024

Hello Kay,
my solution (#1697) for your problem caused a problem (#1743) on Arch-based systems. There /run/lock/ is not writeable by regular users.

So I have to think about an alternative solution and thinking about use cases where my new solution might be a problem. What do you think?
#1743 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Discussion decision or consensus needed Reproduced
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants