Skip to content

Commit a4f8c75

Browse files
committed
♻️ Emit owner facets from xtask converters
1 parent 1760cc6 commit a4f8c75

1 file changed

Lines changed: 93 additions & 49 deletions

File tree

xtask/src/main.rs

Lines changed: 93 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -294,13 +294,13 @@ fn convert_entry<R: Read, W: Write>(
294294
0
295295
});
296296
let mtime_duration = libpna::Duration::new(mtime as i64, 0);
297-
let permission = build_permission(header, &path);
297+
let owner = TarOwner::from_header(header, &path);
298298
let entry_name = libpna::EntryName::from_utf8_preserve_root(&path);
299299

300300
if entry_type.is_dir() {
301301
let mut builder = libpna::EntryBuilder::new_dir(entry_name);
302302
builder.modified(mtime_duration);
303-
builder.permission(permission);
303+
owner.apply(&mut builder);
304304
archive.add_entry(builder.build()?)?;
305305
} else if entry_type.is_symlink() {
306306
let link = entry
@@ -313,7 +313,7 @@ fn convert_entry<R: Read, W: Write>(
313313
libpna::EntryReference::from_utf8_preserve_root(&link),
314314
)?;
315315
builder.modified(mtime_duration);
316-
builder.permission(permission);
316+
owner.apply(&mut builder);
317317
archive.add_entry(builder.build()?)?;
318318
} else if entry_type.is_hard_link() {
319319
let link = entry
@@ -326,13 +326,13 @@ fn convert_entry<R: Read, W: Write>(
326326
libpna::EntryReference::from_utf8_preserve_root(&link),
327327
)?;
328328
builder.modified(mtime_duration);
329-
builder.permission(permission);
329+
owner.apply(&mut builder);
330330
archive.add_entry(builder.build()?)?;
331331
} else if entry_type.is_file() {
332332
let mut builder = libpna::EntryBuilder::new_file(entry_name, write_options)?;
333333
io::copy(entry, &mut builder)?;
334334
builder.modified(mtime_duration);
335-
builder.permission(permission);
335+
owner.apply(&mut builder);
336336
archive.add_entry(builder.build()?)?;
337337
} else {
338338
eprintln!(
@@ -344,36 +344,86 @@ fn convert_entry<R: Read, W: Write>(
344344
Ok(())
345345
}
346346

347-
fn build_permission(header: &tar::Header, path: &str) -> libpna::Permission {
348-
let uid = header.uid().unwrap_or_else(|e| {
349-
eprintln!("warning: {path}: failed to read uid ({e}), defaulting to 0");
350-
0
351-
});
352-
let gid = header.gid().unwrap_or_else(|e| {
353-
eprintln!("warning: {path}: failed to read gid ({e}), defaulting to 0");
354-
0
355-
});
356-
let mode = (header.mode().unwrap_or_else(|e| {
357-
eprintln!("warning: {path}: failed to read mode ({e}), defaulting to 0o644");
358-
0o644
359-
}) & 0o7777) as u16;
360-
let uname = match header.username() {
361-
Ok(Some(name)) => name.to_string(),
362-
Ok(None) => String::new(),
363-
Err(e) => {
364-
eprintln!("warning: {path}: username is not valid UTF-8 ({e})");
365-
String::new()
366-
}
367-
};
368-
let gname = match header.groupname() {
369-
Ok(Some(name)) => name.to_string(),
370-
Ok(None) => String::new(),
371-
Err(e) => {
372-
eprintln!("warning: {path}: groupname is not valid UTF-8 ({e})");
373-
String::new()
347+
fn owner_name_opt(s: &str) -> Option<libpna::OwnerUserName> {
348+
owner_name_bounded(s)
349+
.map(|t| libpna::OwnerUserName::new(t).expect("owner_name_bounded guarantees <= 255 bytes"))
350+
}
351+
352+
fn owner_group_name_opt(s: &str) -> Option<libpna::OwnerGroupName> {
353+
owner_name_bounded(s).map(|t| {
354+
libpna::OwnerGroupName::new(t).expect("owner_name_bounded guarantees <= 255 bytes")
355+
})
356+
}
357+
358+
fn owner_name_bounded(s: &str) -> Option<&str> {
359+
if s.is_empty() {
360+
return None;
361+
}
362+
const MAX: usize = u8::MAX as usize;
363+
if s.len() <= MAX {
364+
return Some(s);
365+
}
366+
let mut end = MAX;
367+
while !s.is_char_boundary(end) {
368+
end -= 1;
369+
}
370+
Some(&s[..end])
371+
}
372+
373+
struct TarOwner {
374+
uid: u64,
375+
gid: u64,
376+
mode: u16,
377+
uname: String,
378+
gname: String,
379+
}
380+
381+
impl TarOwner {
382+
fn from_header(header: &tar::Header, path: &str) -> Self {
383+
let uid = header.uid().unwrap_or_else(|e| {
384+
eprintln!("warning: {path}: failed to read uid ({e}), defaulting to 0");
385+
0
386+
});
387+
let gid = header.gid().unwrap_or_else(|e| {
388+
eprintln!("warning: {path}: failed to read gid ({e}), defaulting to 0");
389+
0
390+
});
391+
let mode = (header.mode().unwrap_or_else(|e| {
392+
eprintln!("warning: {path}: failed to read mode ({e}), defaulting to 0o644");
393+
0o644
394+
}) & 0o7777) as u16;
395+
let uname = match header.username() {
396+
Ok(Some(name)) => name.to_string(),
397+
Ok(None) => String::new(),
398+
Err(e) => {
399+
eprintln!("warning: {path}: username is not valid UTF-8 ({e})");
400+
String::new()
401+
}
402+
};
403+
let gname = match header.groupname() {
404+
Ok(Some(name)) => name.to_string(),
405+
Ok(None) => String::new(),
406+
Err(e) => {
407+
eprintln!("warning: {path}: groupname is not valid UTF-8 ({e})");
408+
String::new()
409+
}
410+
};
411+
Self {
412+
uid,
413+
gid,
414+
mode,
415+
uname,
416+
gname,
374417
}
375-
};
376-
libpna::Permission::new(uid, uname, gid, gname, mode)
418+
}
419+
420+
fn apply(&self, builder: &mut libpna::EntryBuilder) {
421+
builder.owner_uid(libpna::OwnerUid::from(self.uid));
422+
builder.owner_gid(libpna::OwnerGid::from(self.gid));
423+
builder.permission_mode(libpna::PermissionMode::from(self.mode));
424+
builder.owner_user_name(owner_name_opt(&self.uname));
425+
builder.owner_group_name(owner_group_name_opt(&self.gname));
426+
}
377427
}
378428

379429
fn zip2pna(args: Zip2pnaArgs) -> Result<(), Box<dyn std::error::Error>> {
@@ -414,15 +464,12 @@ fn convert_zip_entry<R: Read + io::Seek, W: Write>(
414464
) -> Result<(), Box<dyn std::error::Error>> {
415465
let path = entry.name().to_string();
416466
let mtime = zip_last_modified(entry, &path);
417-
let permission = build_zip_permission(entry);
418467
let entry_name = libpna::EntryName::from_utf8_preserve_root(&path);
419468

420469
if entry.is_dir() {
421470
let mut builder = libpna::EntryBuilder::new_dir(entry_name);
422471
builder.modified(mtime);
423-
if let Some(perm) = permission {
424-
builder.permission(perm);
425-
}
472+
apply_zip_owner(&mut builder, entry);
426473
archive.add_entry(builder.build()?)?;
427474
} else if entry.is_symlink() {
428475
let mut target = String::new();
@@ -432,17 +479,13 @@ fn convert_zip_entry<R: Read + io::Seek, W: Write>(
432479
libpna::EntryReference::from_utf8_preserve_root(&target),
433480
)?;
434481
builder.modified(mtime);
435-
if let Some(perm) = permission {
436-
builder.permission(perm);
437-
}
482+
apply_zip_owner(&mut builder, entry);
438483
archive.add_entry(builder.build()?)?;
439484
} else if entry.is_file() {
440485
let mut builder = libpna::EntryBuilder::new_file(entry_name, write_options)?;
441486
io::copy(entry, &mut builder)?;
442487
builder.modified(mtime);
443-
if let Some(perm) = permission {
444-
builder.permission(perm);
445-
}
488+
apply_zip_owner(&mut builder, entry);
446489
archive.add_entry(builder.build()?)?;
447490
} else {
448491
eprintln!("warning: skipping unsupported entry: {path}");
@@ -476,11 +519,12 @@ fn zip_last_modified<R: Read + io::Seek>(
476519
}
477520
}
478521

479-
fn build_zip_permission<R: Read + io::Seek>(
522+
fn apply_zip_owner<R: Read + io::Seek>(
523+
builder: &mut libpna::EntryBuilder,
480524
entry: &zip::read::ZipFile<'_, R>,
481-
) -> Option<libpna::Permission> {
482-
entry.unix_mode().map(|mode| {
525+
) {
526+
if let Some(mode) = entry.unix_mode() {
483527
let mode_bits = (mode & 0o7777) as u16;
484-
libpna::Permission::new(0, String::new(), 0, String::new(), mode_bits)
485-
})
528+
builder.permission_mode(libpna::PermissionMode::from(mode_bits));
529+
}
486530
}

0 commit comments

Comments
 (0)