/******************************************************/ /* tpax: a topological pax implementation */ /* Copyright (C) 2020 Z. Gilboa */ /* Released under GPLv2 and GPLv3; see COPYING.TPAX. */ /******************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include "tpax_driver_impl.h" #ifndef ssizeof #define ssizeof(x) (ssize_t)(sizeof(x)) #endif static void tpax_octal_write(char * ch, ssize_t len, uint64_t val) { for (--len,--len; len>=0 ; len--) { ch[len] = val % 8 + '0'; val /= 8; } } int tpax_init_ustar_header( const struct tpax_driver_ctx * dctx, const char * path, const struct stat * st, const char * linkname, struct tpax_ustar_header * uhdr) { size_t len; const char * cap; const char * mark; unsigned char * uch; unsigned char * uchcap; size_t lnklen; size_t stsize; int64_t stmtim; uint32_t chksum; char typeflag; struct group grp; struct group * grpres; struct passwd pwd; struct passwd * pwdres; char pwdbuf[2048]; /* size & mtime validation */ stsize = S_ISREG(st->st_mode) ? st->st_size : 0; stmtim = st->st_mtim.tv_sec; if (stsize > 077777777777) return -1; if ((stmtim < 0) || (stmtim > 077777777777)) return -1; /* linkname validation */ if (S_ISLNK(st->st_mode) && !linkname) return -1; lnklen = S_ISLNK(st->st_mode) ? strnlen(linkname,sizeof(uhdr->u_linkname) + 1) : 0; if (lnklen > sizeof(uhdr->u_linkname)) return -1; /* typeflag validation */ if (S_ISREG(st->st_mode)) typeflag = TPAX_USTAR_TYPEFLAG_REGFILE; else if (S_ISLNK(st->st_mode)) typeflag = TPAX_USTAR_TYPEFLAG_SYMLINK; else if (S_ISDIR(st->st_mode)) typeflag = TPAX_USTAR_TYPEFLAG_DIRFILE; else if (S_ISCHR(st->st_mode)) typeflag = TPAX_USTAR_TYPEFLAG_CHARDEV; else if (S_ISBLK(st->st_mode)) typeflag = TPAX_USTAR_TYPEFLAG_BLKDEV; else if (S_ISFIFO(st->st_mode)) typeflag = TPAX_USTAR_TYPEFLAG_FIFODEV; else return -1; /* cap (without the trailing slash) */ mark = &path[strlen(path)]; cap = mark; for (--cap; (*cap == '/') && (cap > path); cap--) (void)0; cap = ((cap == path) && (path[0] == '/')) ? mark : &cap[1]; /* path solely consists of slash symbols? */ if ((cap == mark) && (path[0] =='/')) { if ((cap - path) > (ssizeof(uhdr->u_prefix) + 1 + ssizeof(uhdr->u_name))) return -1; else if ((cap - path) <= ssizeof(uhdr->u_name)) mark = 0; else mark -= sizeof(uhdr->u_name); /* path entirely fits in u_name? */ } else if ((cap - path) <= ssizeof(uhdr->u_name)) { mark = 0; /* split between u_prefix and u_name as needed */ } else { mark = cap; do { for (--mark; (mark > path) && (*mark != '/'); mark--) (void)0; } while ((mark - path) > ssizeof(uhdr->u_prefix)); } /* one shot */ memset(uhdr,0,sizeof(*uhdr)); /* u_name, u_prefix */ if (mark) { memcpy(uhdr->u_prefix,path,mark-path); memcpy(uhdr->u_name,&mark[1],cap - mark); } else { memcpy(uhdr->u_name,path,cap - path); } /* u_mode */ tpax_octal_write(uhdr->u_mode,ssizeof(uhdr->u_mode),st->st_mode & TPAX_USTAR_MODE_MASK); /* u_uid, u_gid, u_uname, u_gname */ if (dctx->cctx->drvflags & TPAX_DRIVER_WRITE_FORMAT_RUSTAR) { tpax_octal_write(uhdr->u_uid,ssizeof(uhdr->u_uid),0); tpax_octal_write(uhdr->u_gid,ssizeof(uhdr->u_gid),0); } else { if ((uint64_t)st->st_uid <= 07777777) tpax_octal_write(uhdr->u_uid,ssizeof(uhdr->u_uid),st->st_uid); else tpax_octal_write(uhdr->u_uid,ssizeof(uhdr->u_uid),0); if ((uint64_t)st->st_gid <= 07777777) tpax_octal_write(uhdr->u_gid,ssizeof(uhdr->u_gid),st->st_gid); else tpax_octal_write(uhdr->u_gid,ssizeof(uhdr->u_gid),0); pwdres = 0; grpres = 0; getpwuid_r( st->st_uid,&pwd, pwdbuf,sizeof(pwdbuf), &pwdres); if (pwdres && pwd.pw_name) if ((len = strlen(pwd.pw_name)) < sizeof(uhdr->u_uname)) memcpy(uhdr->u_uname,pwd.pw_name,len); getgrgid_r( st->st_gid,&grp, pwdbuf,sizeof(pwdbuf), &grpres); if (grpres && grp.gr_name) if ((len = strlen(grp.gr_name)) < sizeof(uhdr->u_gname)) memcpy(uhdr->u_gname,grp.gr_name,len); } /* u_size, u_mtime */ tpax_octal_write(uhdr->u_size,ssizeof(uhdr->u_size),stsize); tpax_octal_write(uhdr->u_mtime,ssizeof(uhdr->u_mtime),stmtim); /* u_typeflag */ uhdr->u_typeflag[0] = typeflag; /* u_linkname */ if (lnklen) memcpy(uhdr->u_linkname,linkname,lnklen); /* u_magic */ uhdr->u_magic[0] = 'u'; uhdr->u_magic[1] = 's'; uhdr->u_magic[2] = 't'; uhdr->u_magic[3] = 'a'; uhdr->u_magic[4] = 'r'; /* u_version */ uhdr->u_version[0] = '0'; uhdr->u_version[1] = '0'; /* u_devmajor, u_devminor */ tpax_octal_write(uhdr->u_devmajor,ssizeof(uhdr->u_devmajor),0); tpax_octal_write(uhdr->u_devminor,ssizeof(uhdr->u_devminor),0); /* u_chksum */ uch = (unsigned char *)uhdr->u_name; uchcap = (unsigned char *)uhdr->u_pad; for (chksum=0; uchu_chksum) * ' '; tpax_octal_write(uhdr->u_chksum,ssizeof(uhdr->u_chksum),chksum); /* all done; caller may now change REGFILE to HARDLINK */ return 0; }