"Backup::Error: Restore operation failed: gtar: .: Cannot unlink: Invalid argument" errors during restore on XFS filesystems containing "./"
Summary
GitLab backup restore fails on XFS filesystems with the error gtar: .: Cannot unlink: Invalid argument when extracting tar archives that contain a ./ (current directory) entry. This is because XFS handles directory unlinking differently than ext4, and the --unlink-first tar option used during restore attempts to unlink the current directory, which XFS rejects with EINVAL.
There have been at least 3 customer reports. I was working on this one
Steps to reproduce
- Set up a GitLab Omnibus installation on a system using XFS filesystem for
/var/opt/gitlab - Create a GitLab backup:
/opt/gitlab/bin/gitlab-backup create - Attempt to restore the backup:
BACKUP="<backup_id>" force=yes /opt/gitlab/bin/gitlab-backup restore
What is the current bug behavior?
The restore process fails during the uploads restoration phase with:
rake aborted!
Backup::Error: Restore operation failed: gtar: .: Cannot unlink: Invalid argument
gtar: Exiting with failure status due to previous errors
/opt/gitlab/embedded/service/gitlab-rails/lib/backup/targets/files.rb:104:in `restore'
The backup tar archives contain a ./ entry:
$ tar -tvf uploads.tar.gz
drwxr-xr-x git/git 0 2026-01-20 20:35 ./
...
When tar uses the --unlink-first option and encounters this entry on XFS, it attempts to call unlink(".") which XFS rejects with EINVAL (Invalid argument). This is valid filesystem behavior - XFS is stricter about this operation than ext4.
What is the expected correct behavior?
The restore process should complete successfully on XFS filesystems by treating the gtar: .: Cannot unlink: Invalid argument warning as non-critical, similar to how other non-critical tar warnings are already handled.
Relevant logs and/or screenshots
2026-01-14 11:22:48 +0100 -- Restoring repositories ... done
2026-01-14 11:22:48 +0100 -- Restoring uploads ...
rake aborted!
Backup::Error: Restore operation failed: gtar: .: Cannot unlink: Invalid argument
gtar: Exiting with failure status due to previous errors
/opt/gitlab/embedded/service/gitlab-rails/lib/backup/targets/files.rb:104:in `restore'
/opt/gitlab/embedded/service/gitlab-rails/lib/backup/tasks/task.rb:31:in `restore!'
/opt/gitlab/embedded/service/gitlab-rails/lib/backup/restore/process.rb:30:in `execute!'
/opt/gitlab/embedded/service/gitlab-rails/lib/backup/manager.rb:101:in `run_restore_task'
...
Environment
- GitLab Omnibus installation
- AlmaLinux 9.7
- XFS filesystem on
/var/opt/gitlab - SELinux enabled (confirmed not the cause)
Output of checks
This bug happens on GitLab Self-Managed (Omnibus).
Results of GitLab environment info
Filesystem Type 1K-blocks Used Available Use% Mounted on
/dev/sdb1 xfs 2146696176 1380857340 765838836 65% /var/opt/gitlab
Proposed fix
Add the XFS-specific unlink error to the noncritical_warnings list in lib/backup/targets/files.rb:
def noncritical_warning?(warning)
noncritical_warnings = [
/^g?tar: \.: Cannot mkdir: No such file or directory$/,
/^g?tar: \.: Cannot unlink: Invalid argument$/ # XFS filesystem behavior
]
noncritical_warnings.map { |w| warning =~ w }.any?
end
This approach is consistent with the existing handling of the Cannot mkdir warning (see #22442 (closed)) and allows the restore to complete successfully since the ./ entry doesn't need to be unlinked for the restore to work correctly.
Workaround
Users can manually patch lib/backup/targets/files.rb to add the warning pattern to the noncritical_warnings array, or skip the affected restore targets and manually extract them:
BACKUP="<backup_id>" SKIP=uploads force=yes /opt/gitlab/bin/gitlab-backup restore
# Then manually extract uploads
cd /var/opt/gitlab/backups
sudo -u git tar -xzf uploads.tar.gz -C /var/opt/gitlab/gitlab-rails/uploads --no-overwrite-dir
Note: The --no-overwrite-dir flag cannot be used with --unlink-first, so the workaround requires manual extraction.