如何在 tar 存档中重新映射 uid/gid?

How to remap uid/gid in tar archive?

我有包含文件的文件夹:

 UID     GID 
10000   10000   ./file1.txt
10000   10000   ./file2.txt
10400   10400   ./file3.txt
10402   10402   ./file4.txt
10052   10052   ./file5.txt

我想创建 tar.bz2 存档,下一个 UID/GID:

UID     GID 
0   0    ./file1.txt
0   0    ./file2.txt
400 400  ./file3.txt
402 402  ./file4.txt
52  52   ./file5.txt

我想从所有 uid 和 gid 中减去 10000,并将结果保存为 tar.bz2.

我该怎么做?

想必您知道您需要 root 权限才能更改 UID/GID。以下脚本将起作用。可以通过使用命令行参数来指定输入目录和输出 .tar.bz2 文件名.

来改进它
#!/bin/bash

for file in `find . -type f -print`
do
    UID=`ls -lnd "$file" | awk '{print }'`
    GID=`ls -lnd "$file" | awk '{print }'`
    NEWUID=`expr $UID - 10000`
    NEWGID=`expr $GID - 10000`
    if [ "$NEWUID" -ge 0  -a "$NEWGID" -ge 0 ]; then
        echo chown ${NEWUID}:${NEWGID} "$file"
        chown ${NEWUID}:${NEWGID} "$file"
    fi
done
tar cvjSf /tmp/remap.tar.bz2 .

以下 Python 脚本在标准输入上读取 tarball 并从其所有成员的 uid 和 gid 中减去 10000:

#!/usr/bin/env python3
import tarfile
import sys

with tarfile.open(fileobj=sys.stdin.buffer, mode="r|*") as in_tar, \
    tarfile.open(fileobj=sys.stdout.buffer, mode="w|", format=tarfile.PAX_FORMAT) as out_tar:
    for member in in_tar:
        member.uid = member.uid-10000
        member.gid = member.gid-10000
        if member.isfile():
            with in_tar.extractfile(member) as file:
                out_tar.addfile(member, file)
        else:
            out_tar.addfile(member)

编辑:这个post的早期版本省略了tarfile.open()format=tarfile.PAX_FORMAT参数。这意味着输出 tarball 将不包含 Python 3.7 及更早版本的输入 tarball 的 PAX headers 中可能包含的扩展属性。从 Python 3.8 开始,PAX_FORMAT 是默认值。

此 post 的其余部分留作历史用途。

这个Python脚本的问题是,tarball模块不支持扩展属性(xattr,SCHILY),因此不适合转换根文件系统的tarball,这是想要转移 uid/gid 个号码。这是一个有效的 go 脚本:

package main    

import (
        "archive/tar"
        "io"
        "log"
        "os"
)

func main() {
        tr := tar.NewReader(os.Stdin)
        tw := tar.NewWriter(os.Stdout)

        for {
                hdr, err := tr.Next()
                if err == io.EOF {
                        break
                } else if err != nil {
                        log.Fatal(err)
                }

                hdr.Uid -= 10000
                hdr.Gid -= 10000
                if err := tw.WriteHeader(hdr); err != nil {
                        log.Fatal(err)
                }

                if hdr.Typeflag == tar.TypeReg {
                        if _, err := io.Copy(tw, tr); err != nil {
                                log.Fatal(err)
                        }
                }
        }

        if err := tw.Close(); err != nil {
                log.Fatal(err)
        }
}

运行 用 go run script.go 或者用 go build script.go.

编译