#!/bin/sh # lib/usuarios-del-home.sh — los usuarios que ya existen en un `/home`, llevados al sistema nuevo. # # Lo comparten los dos caminos que conservan datos de alguien: `--keep` (el /home está en otra # partición) y `--adopt` (el /home está dentro de la partición que se adopta). Es la misma pregunta # —cómo hace el sistema nuevo para que esos archivos sigan siendo de quien son— y por eso hay una # sola respuesta: dos implementaciones de esto divergen en el caso raro, y el síntoma es un # escritorio que no puede escribir en su propia carpeta. # # Vive aparte para poder certificarla sin root, igual que `lib/adoptar.sh`. # cosechar_usuarios — un usuario por DIRECTORIO de primer nivel, con # el dueño de ese directorio. Es el mismo criterio para el `/home` que está en otra partición # (`--keep`) y para el que está DENTRO de la partición que se adopta (`--adopt`): el uid de los # datos manda, y el login es el nombre del directorio. Una sola copia, porque dos criterios de # cosecha que se separan dan dos juegos de usuarios distintos para el mismo disco. cosechar_usuarios() { _cu_dir="$1"; _cu_out="$2" : > "$_cu_out" for _cu_h in "$_cu_dir"/*; do [ -d "$_cu_h" ] || continue _cu_n=$(basename "$_cu_h") [ "$_cu_n" = "lost+found" ] && continue # Un login que no es un login se rechaza NOMBRÁNDOLO, no se salta en silencio: un directorio # con un nombre raro en /home es justo el que alguien va a echar de menos. case "$_cu_n" in *[!a-z0-9_-]*|-*|"") echo " ⚠ «$_cu_n» no es un nombre de usuario válido: no creo usuario para ese directorio (los datos NO se tocan)." ;; *) _cu_u=$(stat -c %u "$_cu_h" 2>/dev/null || echo "") _cu_g=$(stat -c %g "$_cu_h" 2>/dev/null || echo "") case "${_cu_u:-x}${_cu_g:-x}" in *x*) echo " ⚠ no pude leer el dueño de $_cu_h: lo salto." ; continue ;; esac [ "$_cu_u" = "0" ] && { echo " ⚠ $_cu_h es de root (uid 0): no creo usuario, y los datos NO se tocan."; continue; } echo "${_cu_n}:${_cu_u}:${_cu_g}" >> "$_cu_out" ;; esac done } # usuarios_aplicar <árbol> — crea en la raíz NUEVA los usuarios que ya existen en el # `/home` que se conserva, **con el uid que tienen sus archivos**. Lo comparten `--keep` (el /home # en otra partición) y `--adopt` (el /home dentro de la partición que se adopta): es la misma # pregunta —cómo hace el sistema nuevo para que esos archivos sigan siendo de quien son— y una sola # respuesta, porque dos implementaciones de esto divergen en el caso raro y el síntoma es un # escritorio que no puede escribir en su propia carpeta. usuarios_aplicar() { _ua_t="$1"; _ua_lista="$2" # ⚠ **Nada se edita EN SITIO.** El árbol del stage son HARDLINKS del rootfs sellado (ADR 0005): # un `>>` sobre `$_ua_t/etc/passwd` escribiría dentro del ARTEFACTO DEL STORE y envenenaría el # corpus para todo el mundo, sin que nada falle. Se escribe un fichero nuevo y se `mv` encima, # que rompe el enlace y deja el inode del store intacto. for _ua_f in etc/passwd etc/group etc/shadow; do [ -e "$_ua_t/$_ua_f" ] || continue cp "$_ua_t/$_ua_f" "$_ua_t/$_ua_f.keep-tmp" mv "$_ua_t/$_ua_f.keep-tmp" "$_ua_t/$_ua_f" done mkdir -p "$_ua_t/etc" "$_ua_t/home" [ -e "$_ua_t/etc/passwd" ] || : > "$_ua_t/etc/passwd" [ -e "$_ua_t/etc/group" ] || : > "$_ua_t/etc/group" # 1) Los usuarios, con SU uid. Un choque de uid con un usuario de sistema (sshd es 74) no se # arregla solo: se aborta ANTES de formatear y se dice cuál, porque la salida es del usuario # —renombrar o renumerar— y no nuestra. while IFS=':' read -r _ua_n _ua_u _ua_g; do [ -n "$_ua_n" ] || continue _ua_choque=$(awk -F: -v u="$_ua_u" -v n="$_ua_n" '$3==u && $1!=n {print $1; exit}' "$_ua_t/etc/passwd") [ -n "$_ua_choque" ] && { echo "takana-install-partition: el uid $_ua_u del /home conservado (usuario «$_ua_n») ya es de" >&2 echo " «$_ua_choque» en el sistema nuevo. No sigo, y NADA se formateó." >&2 echo " Salidas: renombrá ese directorio, o instalá sin --keep y copiá a mano." >&2 exit 1; } if awk -F: -v n="$_ua_n" '$1==n {found=1} END {exit !found}' "$_ua_t/etc/passwd"; then echo " · «$_ua_n» ya existe en el sistema nuevo: no lo vuelvo a crear." continue fi printf '%s:x:%s:%s:%s:/home/%s:/bin/sh\n' "$_ua_n" "$_ua_u" "$_ua_g" "$_ua_n" "$_ua_n" >> "$_ua_t/etc/passwd" awk -F: -v g="$_ua_g" 'BEGIN{r=1} $3==g {r=0} END{exit r}' "$_ua_t/etc/group" \ || printf '%s:x:%s:\n' "$_ua_n" "$_ua_g" >> "$_ua_t/etc/group" # Contraseña BLOQUEADA, no vacía: `!` no casa con ningún hash, así que no hay login por # consola hasta que alguien le ponga una. Una entrada con campo vacío es login SIN CLAVE. [ -e "$_ua_t/etc/shadow" ] && printf '%s:!:20000:0:99999:7:::\n' "$_ua_n" >> "$_ua_t/etc/shadow" echo " · usuario «$_ua_n» creado con uid $_ua_u (el mismo que tienen sus archivos)" done < "$_ua_lista" }