@@ -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
379429fn 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