LCOV - code coverage report
Current view: top level - lib/dpkg - compress.c (source / functions) Coverage Total Hit
Test: dpkg 1.22.7-3-g89f48 C code coverage Lines: 64.0 % 519 332
Test Date: 2024-07-17 02:53:43 Functions: 80.9 % 47 38
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 42.4 % 262 111

             Branch data     Line data    Source code
       1                 :             : /*
       2                 :             :  * libdpkg - Debian packaging suite library routines
       3                 :             :  * compress.c - compression support functions
       4                 :             :  *
       5                 :             :  * Copyright © 2000 Wichert Akkerman <wakkerma@debian.org>
       6                 :             :  * Copyright © 2004 Scott James Remnant <scott@netsplit.com>
       7                 :             :  * Copyright © 2006-2023 Guillem Jover <guillem@debian.org>
       8                 :             :  *
       9                 :             :  * This is free software; you can redistribute it and/or modify
      10                 :             :  * it under the terms of the GNU General Public License as published by
      11                 :             :  * the Free Software Foundation; either version 2 of the License, or
      12                 :             :  * (at your option) any later version.
      13                 :             :  *
      14                 :             :  * This is distributed in the hope that it will be useful,
      15                 :             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16                 :             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17                 :             :  * GNU General Public License for more details.
      18                 :             :  *
      19                 :             :  * You should have received a copy of the GNU General Public License
      20                 :             :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21                 :             :  */
      22                 :             : 
      23                 :             : #include <config.h>
      24                 :             : #include <compat.h>
      25                 :             : 
      26                 :             : #include <errno.h>
      27                 :             : #include <string.h>
      28                 :             : #include <unistd.h>
      29                 :             : #include <stdbool.h>
      30                 :             : #include <stdlib.h>
      31                 :             : 
      32                 :             : #if USE_LIBZ_IMPL != USE_LIBZ_IMPL_NONE
      33                 :             : #include <compat-zlib.h>
      34                 :             : #endif
      35                 :             : #ifdef WITH_LIBLZMA
      36                 :             : #include <lzma.h>
      37                 :             : #endif
      38                 :             : #ifdef WITH_LIBZSTD
      39                 :             : #include <zstd.h>
      40                 :             : #define DPKG_ZSTD_MAX_LEVEL ZSTD_maxCLevel()
      41                 :             : #else
      42                 :             : #define DPKG_ZSTD_MAX_LEVEL 22
      43                 :             : #define ZSTD_CLEVEL_DEFAULT 3
      44                 :             : #endif
      45                 :             : #define DPKG_ZSTD_CLEVEL_DEFAULT ZSTD_CLEVEL_DEFAULT
      46                 :             : #ifdef WITH_LIBBZ2
      47                 :             : #include <bzlib.h>
      48                 :             : #endif
      49                 :             : 
      50                 :             : #include <dpkg/i18n.h>
      51                 :             : #include <dpkg/dpkg.h>
      52                 :             : #include <dpkg/error.h>
      53                 :             : #include <dpkg/varbuf.h>
      54                 :             : #include <dpkg/fdio.h>
      55                 :             : #include <dpkg/buffer.h>
      56                 :             : #include <dpkg/meminfo.h>
      57                 :             : #include <dpkg/command.h>
      58                 :             : #include <dpkg/compress.h>
      59                 :             : #if USE_LIBZ_IMPL == USE_LIBZ_IMPL_NONE || \
      60                 :             :     !defined(WITH_LIBLZMA) || \
      61                 :             :     !defined(WITH_LIBZSTD) || \
      62                 :             :     !defined(WITH_LIBBZ2)
      63                 :             : #include <dpkg/subproc.h>
      64                 :             : 
      65                 :             : static void
      66                 :             : fd_fd_filter(struct command *cmd, int fd_in, int fd_out, const char *delenv[])
      67                 :             : {
      68                 :             :         pid_t pid;
      69                 :             : 
      70                 :             :         pid = subproc_fork();
      71                 :             :         if (pid == 0) {
      72                 :             :                 int i;
      73                 :             : 
      74                 :             :                 if (fd_in != 0) {
      75                 :             :                         m_dup2(fd_in, 0);
      76                 :             :                         close(fd_in);
      77                 :             :                 }
      78                 :             :                 if (fd_out != 1) {
      79                 :             :                         m_dup2(fd_out, 1);
      80                 :             :                         close(fd_out);
      81                 :             :                 }
      82                 :             : 
      83                 :             :                 for (i = 0; delenv[i]; i++)
      84                 :             :                         unsetenv(delenv[i]);
      85                 :             : 
      86                 :             :                 command_exec(cmd);
      87                 :             :         }
      88                 :             :         subproc_reap(pid, cmd->name, 0);
      89                 :             : }
      90                 :             : 
      91                 :             : static void
      92                 :             : command_compress_init(struct command *cmd, const char *name, const char *desc,
      93                 :             :                       int level)
      94                 :             : {
      95                 :             :         static char combuf[6];
      96                 :             : 
      97                 :             :         command_init(cmd, name, desc);
      98                 :             :         command_add_arg(cmd, name);
      99                 :             : 
     100                 :             :         snprintf(combuf, sizeof(combuf), "-c%d", level);
     101                 :             :         command_add_arg(cmd, combuf);
     102                 :             : }
     103                 :             : 
     104                 :             : static void
     105                 :             : command_decompress_init(struct command *cmd, const char *name, const char *desc)
     106                 :             : {
     107                 :             :         command_init(cmd, name, desc);
     108                 :             :         command_add_arg(cmd, name);
     109                 :             :         command_add_arg(cmd, "-dc");
     110                 :             : }
     111                 :             : #endif
     112                 :             : 
     113                 :             : #if defined(WITH_LIBLZMA) || defined(WITH_LIBZSTD)
     114                 :             : enum dpkg_stream_filter {
     115                 :             :         DPKG_STREAM_COMPRESS    = 1,
     116                 :             :         DPKG_STREAM_DECOMPRESS  = 2,
     117                 :             : };
     118                 :             : 
     119                 :             : enum dpkg_stream_action {
     120                 :             :         DPKG_STREAM_INIT        = 0,
     121                 :             :         DPKG_STREAM_RUN         = 1,
     122                 :             :         DPKG_STREAM_FINISH      = 2,
     123                 :             : };
     124                 :             : 
     125                 :             : enum dpkg_stream_status {
     126                 :             :         DPKG_STREAM_OK,
     127                 :             :         DPKG_STREAM_END,
     128                 :             :         DPKG_STREAM_ERROR,
     129                 :             : };
     130                 :             : #endif
     131                 :             : 
     132                 :             : struct compressor {
     133                 :             :         const char *name;
     134                 :             :         const char *extension;
     135                 :             :         int default_level;
     136                 :             :         void (*fixup_params)(struct compress_params *params);
     137                 :             :         void (*compress)(struct compress_params *params,
     138                 :             :                          int fd_in, int fd_out, const char *desc);
     139                 :             :         void (*decompress)(struct compress_params *params,
     140                 :             :                            int fd_in, int fd_out, const char *desc);
     141                 :             : };
     142                 :             : 
     143                 :             : /*
     144                 :             :  * No compressor (pass-through).
     145                 :             :  */
     146                 :             : 
     147                 :             : static void
     148                 :          76 : fixup_none_params(struct compress_params *params)
     149                 :             : {
     150                 :          76 : }
     151                 :             : 
     152                 :             : static void
     153                 :          10 : decompress_none(struct compress_params *params, int fd_in, int fd_out,
     154                 :             :                 const char *desc)
     155                 :             : {
     156                 :             :         struct dpkg_error err;
     157                 :             : 
     158         [ -  + ]:          10 :         if (fd_fd_copy(fd_in, fd_out, -1, &err) < 0)
     159                 :           0 :                 ohshit(_("%s: pass-through copy error: %s"), desc, err.str);
     160                 :          10 : }
     161                 :             : 
     162                 :             : static void
     163                 :          14 : compress_none(struct compress_params *params, int fd_in, int fd_out,
     164                 :             :               const char *desc)
     165                 :             : {
     166                 :             :         struct dpkg_error err;
     167                 :             : 
     168         [ -  + ]:          14 :         if (fd_fd_copy(fd_in, fd_out, -1, &err) < 0)
     169                 :           0 :                 ohshit(_("%s: pass-through copy error: %s"), desc, err.str);
     170                 :          14 : }
     171                 :             : 
     172                 :             : static const struct compressor compressor_none = {
     173                 :             :         .name = "none",
     174                 :             :         .extension = "",
     175                 :             :         .default_level = 0,
     176                 :             :         .fixup_params = fixup_none_params,
     177                 :             :         .compress = compress_none,
     178                 :             :         .decompress = decompress_none,
     179                 :             : };
     180                 :             : 
     181                 :             : /*
     182                 :             :  * Gzip compressor.
     183                 :             :  */
     184                 :             : 
     185                 :             : #define GZIP            "gzip"
     186                 :             : 
     187                 :             : static void
     188                 :           2 : fixup_gzip_params(struct compress_params *params)
     189                 :             : {
     190                 :             :         /* Normalize compression level. */
     191         [ -  + ]:           2 :         if (params->level == 0)
     192                 :           0 :                 params->type = COMPRESSOR_TYPE_NONE;
     193                 :           2 : }
     194                 :             : 
     195                 :             : #if USE_LIBZ_IMPL != USE_LIBZ_IMPL_NONE
     196                 :             : static void
     197                 :          14 : decompress_gzip(struct compress_params *params, int fd_in, int fd_out,
     198                 :             :                 const char *desc)
     199                 :             : {
     200                 :             :         char *buffer;
     201                 :          14 :         size_t bufsize = DPKG_BUFFER_SIZE;
     202                 :             :         int z_errnum;
     203                 :          14 :         gzFile gzfile = gzdopen(fd_in, "r");
     204                 :             : 
     205         [ -  + ]:          14 :         if (gzfile == NULL)
     206                 :           0 :                 ohshit(_("%s: error binding input to gzip stream"), desc);
     207                 :             : 
     208                 :          14 :         buffer = m_malloc(bufsize);
     209                 :             : 
     210                 :         166 :         for (;;) {
     211                 :             :                 int actualread, actualwrite;
     212                 :             : 
     213                 :         180 :                 actualread = gzread(gzfile, buffer, bufsize);
     214         [ -  + ]:         180 :                 if (actualread < 0) {
     215                 :           0 :                         const char *errmsg = gzerror(gzfile, &z_errnum);
     216                 :             : 
     217         [ #  # ]:           0 :                         if (z_errnum == Z_ERRNO)
     218                 :           0 :                                 errmsg = strerror(errno);
     219                 :           0 :                         ohshit(_("%s: internal gzip read error: '%s'"), desc,
     220                 :             :                                errmsg);
     221                 :             :                 }
     222         [ +  + ]:         180 :                 if (actualread == 0) /* EOF. */
     223                 :          14 :                         break;
     224                 :             : 
     225                 :         166 :                 actualwrite = fd_write(fd_out, buffer, actualread);
     226         [ -  + ]:         166 :                 if (actualwrite != actualread)
     227                 :           0 :                         ohshite(_("%s: internal gzip write error"), desc);
     228                 :             :         }
     229                 :             : 
     230                 :          14 :         free(buffer);
     231                 :             : 
     232                 :          14 :         z_errnum = gzclose(gzfile);
     233         [ -  + ]:          14 :         if (z_errnum) {
     234                 :             :                 const char *errmsg;
     235                 :             : 
     236         [ #  # ]:           0 :                 if (z_errnum == Z_ERRNO)
     237                 :           0 :                         errmsg = strerror(errno);
     238                 :             :                 else
     239                 :           0 :                         errmsg = zError(z_errnum);
     240                 :           0 :                 ohshit(_("%s: internal gzip read error: %s"), desc, errmsg);
     241                 :             :         }
     242                 :             : 
     243         [ -  + ]:          14 :         if (close(fd_out))
     244                 :           0 :                 ohshite(_("%s: internal gzip write error"), desc);
     245                 :          14 : }
     246                 :             : 
     247                 :             : static void
     248                 :           4 : compress_gzip(struct compress_params *params, int fd_in, int fd_out,
     249                 :             :               const char *desc)
     250                 :             : {
     251                 :             :         char *buffer;
     252                 :             :         char combuf[6];
     253                 :           4 :         size_t bufsize = DPKG_BUFFER_SIZE;
     254                 :             :         int strategy;
     255                 :             :         int z_errnum;
     256                 :             :         gzFile gzfile;
     257                 :             : 
     258         [ -  + ]:           4 :         if (params->strategy == COMPRESSOR_STRATEGY_FILTERED)
     259                 :           0 :                 strategy = 'f';
     260         [ -  + ]:           4 :         else if (params->strategy == COMPRESSOR_STRATEGY_HUFFMAN)
     261                 :           0 :                 strategy = 'h';
     262         [ -  + ]:           4 :         else if (params->strategy == COMPRESSOR_STRATEGY_RLE)
     263                 :           0 :                 strategy = 'R';
     264         [ -  + ]:           4 :         else if (params->strategy == COMPRESSOR_STRATEGY_FIXED)
     265                 :           0 :                 strategy = 'F';
     266                 :             :         else
     267                 :           4 :                 strategy = ' ';
     268                 :             : 
     269                 :           4 :         snprintf(combuf, sizeof(combuf), "w%d%c", params->level, strategy);
     270                 :           4 :         gzfile = gzdopen(fd_out, combuf);
     271         [ -  + ]:           4 :         if (gzfile == NULL)
     272                 :           0 :                 ohshit(_("%s: error binding output to gzip stream"), desc);
     273                 :             : 
     274                 :           4 :         buffer = m_malloc(bufsize);
     275                 :             : 
     276                 :          42 :         for (;;) {
     277                 :             :                 int actualread, actualwrite;
     278                 :             : 
     279                 :          46 :                 actualread = fd_read(fd_in, buffer, bufsize);
     280         [ -  + ]:          46 :                 if (actualread < 0)
     281                 :           0 :                         ohshite(_("%s: internal gzip read error"), desc);
     282         [ +  + ]:          46 :                 if (actualread == 0) /* EOF. */
     283                 :           4 :                         break;
     284                 :             : 
     285                 :          42 :                 actualwrite = gzwrite(gzfile, buffer, actualread);
     286         [ -  + ]:          42 :                 if (actualwrite != actualread) {
     287                 :           0 :                         const char *errmsg = gzerror(gzfile, &z_errnum);
     288                 :             : 
     289         [ #  # ]:           0 :                         if (z_errnum == Z_ERRNO)
     290                 :           0 :                                 errmsg = strerror(errno);
     291                 :           0 :                         ohshit(_("%s: internal gzip write error: '%s'"), desc,
     292                 :             :                                errmsg);
     293                 :             :                 }
     294                 :             :         }
     295                 :             : 
     296                 :           4 :         free(buffer);
     297                 :             : 
     298                 :           4 :         z_errnum = gzclose(gzfile);
     299         [ -  + ]:           4 :         if (z_errnum) {
     300                 :             :                 const char *errmsg;
     301                 :             : 
     302         [ #  # ]:           0 :                 if (z_errnum == Z_ERRNO)
     303                 :           0 :                         errmsg = strerror(errno);
     304                 :             :                 else
     305                 :           0 :                         errmsg = zError(z_errnum);
     306                 :           0 :                 ohshit(_("%s: internal gzip write error: %s"), desc, errmsg);
     307                 :             :         }
     308                 :           4 : }
     309                 :             : #else
     310                 :             : static const char *env_gzip[] = { "GZIP", NULL };
     311                 :             : 
     312                 :             : static void
     313                 :             : decompress_gzip(struct compress_params *params, int fd_in, int fd_out,
     314                 :             :                 const char *desc)
     315                 :             : {
     316                 :             :         struct command cmd;
     317                 :             : 
     318                 :             :         command_decompress_init(&cmd, GZIP, desc);
     319                 :             : 
     320                 :             :         fd_fd_filter(&cmd, fd_in, fd_out, env_gzip);
     321                 :             : 
     322                 :             :         command_destroy(&cmd);
     323                 :             : }
     324                 :             : 
     325                 :             : static void
     326                 :             : compress_gzip(struct compress_params *params, int fd_in, int fd_out,
     327                 :             :               const char *desc)
     328                 :             : {
     329                 :             :         struct command cmd;
     330                 :             : 
     331                 :             :         command_compress_init(&cmd, GZIP, desc, params->level);
     332                 :             :         command_add_arg(&cmd, "-n");
     333                 :             : 
     334                 :             :         fd_fd_filter(&cmd, fd_in, fd_out, env_gzip);
     335                 :             : 
     336                 :             :         command_destroy(&cmd);
     337                 :             : }
     338                 :             : #endif
     339                 :             : 
     340                 :             : static const struct compressor compressor_gzip = {
     341                 :             :         .name = "gzip",
     342                 :             :         .extension = ".gz",
     343                 :             :         .default_level = 9,
     344                 :             :         .fixup_params = fixup_gzip_params,
     345                 :             :         .compress = compress_gzip,
     346                 :             :         .decompress = decompress_gzip,
     347                 :             : };
     348                 :             : 
     349                 :             : /*
     350                 :             :  * Bzip2 compressor.
     351                 :             :  */
     352                 :             : 
     353                 :             : #define BZIP2           "bzip2"
     354                 :             : 
     355                 :             : static void
     356                 :           0 : fixup_bzip2_params(struct compress_params *params)
     357                 :             : {
     358                 :             :         /* Normalize compression level. */
     359         [ #  # ]:           0 :         if (params->level == 0)
     360                 :           0 :                 params->level = 1;
     361                 :           0 : }
     362                 :             : 
     363                 :             : #ifdef WITH_LIBBZ2
     364                 :             : static void
     365                 :           1 : decompress_bzip2(struct compress_params *params, int fd_in, int fd_out,
     366                 :             :                  const char *desc)
     367                 :             : {
     368                 :             :         char *buffer;
     369                 :           1 :         size_t bufsize = DPKG_BUFFER_SIZE;
     370                 :           1 :         BZFILE *bzfile = BZ2_bzdopen(fd_in, "r");
     371                 :             : 
     372         [ -  + ]:           1 :         if (bzfile == NULL)
     373                 :           0 :                 ohshit(_("%s: error binding input to bzip2 stream"), desc);
     374                 :             : 
     375                 :           1 :         buffer = m_malloc(bufsize);
     376                 :             : 
     377                 :          20 :         for (;;) {
     378                 :             :                 int actualread, actualwrite;
     379                 :             : 
     380                 :          21 :                 actualread = BZ2_bzread(bzfile, buffer, bufsize);
     381         [ -  + ]:          21 :                 if (actualread < 0) {
     382                 :           0 :                         int bz_errnum = 0;
     383                 :           0 :                         const char *errmsg = BZ2_bzerror(bzfile, &bz_errnum);
     384                 :             : 
     385         [ #  # ]:           0 :                         if (bz_errnum == BZ_IO_ERROR)
     386                 :           0 :                                 errmsg = strerror(errno);
     387                 :           0 :                         ohshit(_("%s: internal bzip2 read error: '%s'"), desc,
     388                 :             :                                errmsg);
     389                 :             :                 }
     390         [ +  + ]:          21 :                 if (actualread == 0) /* EOF. */
     391                 :           1 :                         break;
     392                 :             : 
     393                 :          20 :                 actualwrite = fd_write(fd_out, buffer, actualread);
     394         [ -  + ]:          20 :                 if (actualwrite != actualread)
     395                 :           0 :                         ohshite(_("%s: internal bzip2 write error"), desc);
     396                 :             :         }
     397                 :             : 
     398                 :           1 :         free(buffer);
     399                 :             : 
     400                 :           1 :         BZ2_bzclose(bzfile);
     401                 :             : 
     402         [ -  + ]:           1 :         if (close(fd_out))
     403                 :           0 :                 ohshite(_("%s: internal bzip2 write error"), desc);
     404                 :           1 : }
     405                 :             : 
     406                 :             : static void
     407                 :           0 : compress_bzip2(struct compress_params *params, int fd_in, int fd_out,
     408                 :             :                const char *desc)
     409                 :             : {
     410                 :             :         char *buffer;
     411                 :             :         char combuf[6];
     412                 :           0 :         size_t bufsize = DPKG_BUFFER_SIZE;
     413                 :             :         int bz_errnum;
     414                 :             :         BZFILE *bzfile;
     415                 :             : 
     416                 :           0 :         snprintf(combuf, sizeof(combuf), "w%d", params->level);
     417                 :           0 :         bzfile = BZ2_bzdopen(fd_out, combuf);
     418         [ #  # ]:           0 :         if (bzfile == NULL)
     419                 :           0 :                 ohshit(_("%s: error binding output to bzip2 stream"), desc);
     420                 :             : 
     421                 :           0 :         buffer = m_malloc(bufsize);
     422                 :             : 
     423                 :           0 :         for (;;) {
     424                 :             :                 int actualread, actualwrite;
     425                 :             : 
     426                 :           0 :                 actualread = fd_read(fd_in, buffer, bufsize);
     427         [ #  # ]:           0 :                 if (actualread < 0)
     428                 :           0 :                         ohshite(_("%s: internal bzip2 read error"), desc);
     429         [ #  # ]:           0 :                 if (actualread == 0) /* EOF. */
     430                 :           0 :                         break;
     431                 :             : 
     432                 :           0 :                 actualwrite = BZ2_bzwrite(bzfile, buffer, actualread);
     433         [ #  # ]:           0 :                 if (actualwrite != actualread) {
     434                 :           0 :                         const char *errmsg = BZ2_bzerror(bzfile, &bz_errnum);
     435                 :             : 
     436         [ #  # ]:           0 :                         if (bz_errnum == BZ_IO_ERROR)
     437                 :           0 :                                 errmsg = strerror(errno);
     438                 :           0 :                         ohshit(_("%s: internal bzip2 write error: '%s'"), desc,
     439                 :             :                                errmsg);
     440                 :             :                 }
     441                 :             :         }
     442                 :             : 
     443                 :           0 :         free(buffer);
     444                 :             : 
     445                 :           0 :         BZ2_bzWriteClose(&bz_errnum, bzfile, 0, NULL, NULL);
     446         [ #  # ]:           0 :         if (bz_errnum != BZ_OK) {
     447                 :           0 :                 const char *errmsg = _("unexpected bzip2 error");
     448                 :             : 
     449         [ #  # ]:           0 :                 if (bz_errnum == BZ_IO_ERROR)
     450                 :           0 :                         errmsg = strerror(errno);
     451                 :           0 :                 ohshit(_("%s: internal bzip2 write error: '%s'"), desc,
     452                 :             :                        errmsg);
     453                 :             :         }
     454                 :             : 
     455                 :             :         /* Because BZ2_bzWriteClose has done a fflush on the file handle,
     456                 :             :          * doing a close on the file descriptor associated with it should
     457                 :             :          * be safe™. */
     458         [ #  # ]:           0 :         if (close(fd_out))
     459                 :           0 :                 ohshite(_("%s: internal bzip2 write error"), desc);
     460                 :           0 : }
     461                 :             : #else
     462                 :             : static const char *env_bzip2[] = { "BZIP", "BZIP2", NULL };
     463                 :             : 
     464                 :             : static void
     465                 :             : decompress_bzip2(struct compress_params *params, int fd_in, int fd_out,
     466                 :             :                  const char *desc)
     467                 :             : {
     468                 :             :         struct command cmd;
     469                 :             : 
     470                 :             :         command_decompress_init(&cmd, BZIP2, desc);
     471                 :             : 
     472                 :             :         fd_fd_filter(&cmd, fd_in, fd_out, env_bzip2);
     473                 :             : 
     474                 :             :         command_destroy(&cmd);
     475                 :             : }
     476                 :             : 
     477                 :             : static void
     478                 :             : compress_bzip2(struct compress_params *params, int fd_in, int fd_out,
     479                 :             :                const char *desc)
     480                 :             : {
     481                 :             :         struct command cmd;
     482                 :             : 
     483                 :             :         command_compress_init(&cmd, BZIP2, desc, params->level);
     484                 :             : 
     485                 :             :         fd_fd_filter(&cmd, fd_in, fd_out, env_bzip2);
     486                 :             : 
     487                 :             :         command_destroy(&cmd);
     488                 :             : }
     489                 :             : #endif
     490                 :             : 
     491                 :             : static const struct compressor compressor_bzip2 = {
     492                 :             :         .name = "bzip2",
     493                 :             :         .extension = ".bz2",
     494                 :             :         .default_level = 9,
     495                 :             :         .fixup_params = fixup_bzip2_params,
     496                 :             :         .compress = compress_bzip2,
     497                 :             :         .decompress = decompress_bzip2,
     498                 :             : };
     499                 :             : 
     500                 :             : /*
     501                 :             :  * Xz compressor.
     502                 :             :  */
     503                 :             : 
     504                 :             : #define XZ              "xz"
     505                 :             : 
     506                 :             : #ifdef WITH_LIBLZMA
     507                 :             : struct io_lzma {
     508                 :             :         const char *desc;
     509                 :             : 
     510                 :             :         struct compress_params *params;
     511                 :             : 
     512                 :             :         enum dpkg_stream_filter filter;
     513                 :             :         enum dpkg_stream_action action;
     514                 :             :         enum dpkg_stream_status status;
     515                 :             : 
     516                 :             :         void (*init)(struct io_lzma *io, lzma_stream *s);
     517                 :             :         void (*code)(struct io_lzma *io, lzma_stream *s);
     518                 :             :         void (*done)(struct io_lzma *io, lzma_stream *s);
     519                 :             : };
     520                 :             : 
     521                 :             : /* XXX: liblzma does not expose error messages. */
     522                 :             : static const char *
     523                 :           0 : dpkg_lzma_strerror(struct io_lzma *io, lzma_ret code)
     524                 :             : {
     525                 :           0 :         const char *const impossible = _("internal error (bug)");
     526                 :             : 
     527   [ #  #  #  #  :           0 :         switch (code) {
             #  #  #  # ]
     528                 :           0 :         case LZMA_MEM_ERROR:
     529                 :           0 :                 return strerror(ENOMEM);
     530                 :           0 :         case LZMA_MEMLIMIT_ERROR:
     531         [ #  # ]:           0 :                 if (io->action == DPKG_STREAM_RUN)
     532                 :           0 :                         return _("memory usage limit reached");
     533                 :           0 :                 return impossible;
     534                 :           0 :         case LZMA_OPTIONS_ERROR:
     535         [ #  # ]:           0 :                 if (io->filter == DPKG_STREAM_COMPRESS &&
     536         [ #  # ]:           0 :                     io->action == DPKG_STREAM_INIT)
     537                 :           0 :                         return _("unsupported compression preset");
     538         [ #  # ]:           0 :                 if (io->filter == DPKG_STREAM_DECOMPRESS &&
     539         [ #  # ]:           0 :                     io->action == DPKG_STREAM_RUN)
     540                 :           0 :                         return _("unsupported options in file header");
     541                 :           0 :                 return impossible;
     542                 :           0 :         case LZMA_DATA_ERROR:
     543         [ #  # ]:           0 :                 if (io->action == DPKG_STREAM_RUN)
     544                 :           0 :                         return _("compressed data is corrupt");
     545                 :           0 :                 return impossible;
     546                 :           0 :         case LZMA_BUF_ERROR:
     547         [ #  # ]:           0 :                 if (io->action == DPKG_STREAM_RUN)
     548                 :           0 :                         return _("unexpected end of input");
     549                 :           0 :                 return impossible;
     550                 :           0 :         case LZMA_FORMAT_ERROR:
     551         [ #  # ]:           0 :                 if (io->filter == DPKG_STREAM_DECOMPRESS &&
     552         [ #  # ]:           0 :                     io->action == DPKG_STREAM_RUN)
     553                 :           0 :                         return _("file format not recognized");
     554                 :           0 :                 return impossible;
     555                 :           0 :         case LZMA_UNSUPPORTED_CHECK:
     556         [ #  # ]:           0 :                 if (io->filter == DPKG_STREAM_COMPRESS &&
     557         [ #  # ]:           0 :                     io->action == DPKG_STREAM_INIT)
     558                 :           0 :                         return _("unsupported type of integrity check");
     559                 :           0 :                 return impossible;
     560                 :           0 :         default:
     561                 :           0 :                 return impossible;
     562                 :             :         }
     563                 :             : }
     564                 :             : 
     565                 :             : static void
     566                 :          24 : filter_lzma(struct io_lzma *io, int fd_in, int fd_out)
     567                 :             : {
     568                 :             :         uint8_t *buf_in;
     569                 :             :         uint8_t *buf_out;
     570                 :          24 :         size_t buf_size = DPKG_BUFFER_SIZE;
     571                 :          24 :         lzma_stream s = LZMA_STREAM_INIT;
     572                 :             : 
     573                 :          24 :         buf_in = m_malloc(buf_size);
     574                 :          24 :         buf_out = m_malloc(buf_size);
     575                 :             : 
     576                 :          24 :         s.next_out = buf_out;
     577                 :          24 :         s.avail_out = buf_size;
     578                 :             : 
     579                 :          24 :         io->status = DPKG_STREAM_OK;
     580                 :          24 :         io->action = DPKG_STREAM_INIT;
     581                 :          24 :         io->init(io, &s);
     582                 :          24 :         io->action = DPKG_STREAM_RUN;
     583                 :             : 
     584                 :             :         do {
     585                 :             :                 ssize_t len;
     586                 :             : 
     587   [ +  +  +  + ]:         155 :                 if (s.avail_in == 0 && io->action != DPKG_STREAM_FINISH) {
     588                 :          75 :                         len = fd_read(fd_in, buf_in, buf_size);
     589         [ -  + ]:          75 :                         if (len < 0)
     590                 :           0 :                                 ohshite(_("%s: lzma read error"), io->desc);
     591         [ +  + ]:          75 :                         if (len == 0)
     592                 :          16 :                                 io->action = DPKG_STREAM_FINISH;
     593                 :          75 :                         s.next_in = buf_in;
     594                 :          75 :                         s.avail_in = len;
     595                 :             :                 }
     596                 :             : 
     597                 :         155 :                 io->code(io, &s);
     598                 :             : 
     599   [ +  +  +  + ]:         155 :                 if (s.avail_out == 0 || io->status == DPKG_STREAM_END) {
     600                 :         104 :                         len = fd_write(fd_out, buf_out, s.next_out - buf_out);
     601         [ -  + ]:         104 :                         if (len < 0)
     602                 :           0 :                                 ohshite(_("%s: lzma write error"), io->desc);
     603                 :         104 :                         s.next_out = buf_out;
     604                 :         104 :                         s.avail_out = buf_size;
     605                 :             :                 }
     606         [ +  + ]:         155 :         } while (io->status != DPKG_STREAM_END);
     607                 :             : 
     608                 :          24 :         io->done(io, &s);
     609                 :             : 
     610                 :          24 :         free(buf_in);
     611                 :          24 :         free(buf_out);
     612                 :             : 
     613         [ -  + ]:          24 :         if (close(fd_out))
     614                 :           0 :                 ohshite(_("%s: lzma close error"), io->desc);
     615                 :          24 : }
     616                 :             : 
     617                 :             : static void DPKG_ATTR_NORET
     618                 :           0 : filter_lzma_error(struct io_lzma *io, lzma_ret ret)
     619                 :             : {
     620                 :           0 :         ohshit(_("%s: lzma error: %s"), io->desc,
     621                 :             :                dpkg_lzma_strerror(io, ret));
     622                 :             : }
     623                 :             : 
     624                 :             : #ifdef HAVE_LZMA_MT_ENCODER
     625                 :             : static uint64_t
     626                 :          23 : filter_xz_get_memlimit(void)
     627                 :             : {
     628                 :             :         uint64_t mt_memlimit;
     629                 :             : 
     630                 :             :         /* Ask the kernel what is currently available for us. If this fails
     631                 :             :          * initialize the memory limit to half the physical RAM, or to 128 MiB
     632                 :             :          * if we cannot infer the number. */
     633         [ -  + ]:          23 :         if (meminfo_get_available(&mt_memlimit) < 0) {
     634                 :           0 :                 mt_memlimit = lzma_physmem() / 2;
     635         [ #  # ]:           0 :                 if (mt_memlimit == 0)
     636                 :           0 :                         mt_memlimit = 128 * 1024 * 1024;
     637                 :             :         }
     638                 :             :         /* Clamp the multi-threaded memory limit to half the addressable
     639                 :             :          * memory on this architecture. */
     640         [ -  + ]:          23 :         if (mt_memlimit > INTPTR_MAX)
     641                 :           0 :                 mt_memlimit = INTPTR_MAX;
     642                 :             : 
     643                 :          23 :         return mt_memlimit;
     644                 :             : }
     645                 :             : 
     646                 :             : static uint32_t
     647                 :          23 : filter_xz_get_cputhreads(struct compress_params *params)
     648                 :             : {
     649                 :             :         long threads_max;
     650                 :             : 
     651                 :          23 :         threads_max = lzma_cputhreads();
     652         [ -  + ]:          23 :         if (threads_max == 0)
     653                 :           0 :                 threads_max = 1;
     654                 :             : 
     655         [ -  + ]:          23 :         if (params->threads_max >= 0)
     656         [ #  # ]:           0 :                 return clamp(params->threads_max, 1, threads_max);
     657                 :             : 
     658                 :          23 :         return threads_max;
     659                 :             : }
     660                 :             : #endif
     661                 :             : 
     662                 :             : static void
     663                 :           7 : filter_unxz_init(struct io_lzma *io, lzma_stream *s)
     664                 :             : {
     665                 :             : #ifdef HAVE_LZMA_MT_DECODER
     666                 :           7 :         lzma_mt mt_options = {
     667                 :             :                 .flags = 0,
     668                 :             :                 .block_size = 0,
     669                 :             :                 .timeout = 0,
     670                 :             :                 .filters = NULL,
     671                 :             :         };
     672                 :             : #else
     673                 :             :         uint64_t memlimit = UINT64_MAX;
     674                 :             : #endif
     675                 :             :         lzma_ret ret;
     676                 :             : 
     677                 :           7 :         io->filter = DPKG_STREAM_DECOMPRESS;
     678                 :             : 
     679                 :             : #ifdef HAVE_LZMA_MT_DECODER
     680                 :           7 :         mt_options.memlimit_stop = UINT64_MAX;
     681                 :           7 :         mt_options.memlimit_threading = filter_xz_get_memlimit();
     682                 :           7 :         mt_options.threads = filter_xz_get_cputhreads(io->params);
     683                 :             : 
     684                 :           7 :         ret = lzma_stream_decoder_mt(s, &mt_options);
     685                 :             : #else
     686                 :             :         ret = lzma_stream_decoder(s, memlimit, 0);
     687                 :             : #endif
     688         [ -  + ]:           7 :         if (ret != LZMA_OK)
     689                 :           0 :                 filter_lzma_error(io, ret);
     690                 :           7 : }
     691                 :             : 
     692                 :             : static void
     693                 :          16 : filter_xz_init(struct io_lzma *io, lzma_stream *s)
     694                 :             : {
     695                 :             :         uint32_t preset;
     696                 :          16 :         lzma_check check = LZMA_CHECK_CRC64;
     697                 :             : #ifdef HAVE_LZMA_MT_ENCODER
     698                 :             :         uint64_t mt_memlimit;
     699                 :          16 :         lzma_mt mt_options = {
     700                 :             :                 .flags = 0,
     701                 :             :                 .block_size = 0,
     702                 :             :                 .timeout = 0,
     703                 :             :                 .filters = NULL,
     704                 :             :                 .check = check,
     705                 :             :         };
     706                 :             : #endif
     707                 :             :         lzma_ret ret;
     708                 :             : 
     709                 :          16 :         io->filter = DPKG_STREAM_COMPRESS;
     710                 :             : 
     711                 :          16 :         preset = io->params->level;
     712         [ -  + ]:          16 :         if (io->params->strategy == COMPRESSOR_STRATEGY_EXTREME)
     713                 :           0 :                 preset |= LZMA_PRESET_EXTREME;
     714                 :             : 
     715                 :             : #ifdef HAVE_LZMA_MT_ENCODER
     716                 :          16 :         mt_options.preset = preset;
     717                 :          16 :         mt_memlimit = filter_xz_get_memlimit();
     718                 :          16 :         mt_options.threads = filter_xz_get_cputhreads(io->params);
     719                 :             : 
     720                 :             :         /* Guess whether we have enough RAM to use the multi-threaded encoder,
     721                 :             :          * and decrease them up to single-threaded to reduce memory usage. */
     722         [ +  - ]:          16 :         for (; mt_options.threads > 1; mt_options.threads--) {
     723                 :             :                 uint64_t mt_memusage;
     724                 :             : 
     725                 :          16 :                 mt_memusage = lzma_stream_encoder_mt_memusage(&mt_options);
     726         [ +  - ]:          16 :                 if (mt_memusage < mt_memlimit)
     727                 :          16 :                         break;
     728                 :             :         }
     729                 :             : 
     730                 :          16 :         ret = lzma_stream_encoder_mt(s, &mt_options);
     731                 :             : #else
     732                 :             :         ret = lzma_easy_encoder(s, preset, check);
     733                 :             : #endif
     734                 :             : 
     735         [ -  + ]:          16 :         if (ret != LZMA_OK)
     736                 :           0 :                 filter_lzma_error(io, ret);
     737                 :          16 : }
     738                 :             : 
     739                 :             : static void
     740                 :         155 : filter_lzma_code(struct io_lzma *io, lzma_stream *s)
     741                 :             : {
     742                 :             :         lzma_ret ret;
     743                 :             :         lzma_action action;
     744                 :             : 
     745         [ +  + ]:         155 :         if (io->action == DPKG_STREAM_RUN)
     746                 :         135 :                 action = LZMA_RUN;
     747         [ +  - ]:          20 :         else if (io->action == DPKG_STREAM_FINISH)
     748                 :          20 :                 action = LZMA_FINISH;
     749                 :             :         else
     750                 :           0 :                 internerr("unknown stream filter action %d\n", io->action);
     751                 :             : 
     752                 :         155 :         ret = lzma_code(s, action);
     753                 :             : 
     754         [ +  + ]:         155 :         if (ret == LZMA_STREAM_END)
     755                 :          24 :                 io->status = DPKG_STREAM_END;
     756         [ -  + ]:         131 :         else if (ret != LZMA_OK)
     757                 :           0 :                 filter_lzma_error(io, ret);
     758                 :         155 : }
     759                 :             : 
     760                 :             : static void
     761                 :          24 : filter_lzma_done(struct io_lzma *io, lzma_stream *s)
     762                 :             : {
     763                 :          24 :         lzma_end(s);
     764                 :          24 : }
     765                 :             : 
     766                 :             : static void
     767                 :           7 : decompress_xz(struct compress_params *params, int fd_in, int fd_out,
     768                 :             :               const char *desc)
     769                 :             : {
     770                 :             :         struct io_lzma io;
     771                 :             : 
     772                 :           7 :         io.init = filter_unxz_init;
     773                 :           7 :         io.code = filter_lzma_code;
     774                 :           7 :         io.done = filter_lzma_done;
     775                 :           7 :         io.desc = desc;
     776                 :           7 :         io.params = params;
     777                 :             : 
     778                 :           7 :         filter_lzma(&io, fd_in, fd_out);
     779                 :           7 : }
     780                 :             : 
     781                 :             : static void
     782                 :          16 : compress_xz(struct compress_params *params, int fd_in, int fd_out,
     783                 :             :             const char *desc)
     784                 :             : {
     785                 :             :         struct io_lzma io;
     786                 :             : 
     787                 :          16 :         io.init = filter_xz_init;
     788                 :          16 :         io.code = filter_lzma_code;
     789                 :          16 :         io.done = filter_lzma_done;
     790                 :          16 :         io.desc = desc;
     791                 :          16 :         io.params = params;
     792                 :             : 
     793                 :          16 :         filter_lzma(&io, fd_in, fd_out);
     794                 :          16 : }
     795                 :             : #else
     796                 :             : static const char *env_xz[] = { "XZ_DEFAULTS", "XZ_OPT", NULL };
     797                 :             : 
     798                 :             : static void
     799                 :             : decompress_xz(struct compress_params *params, int fd_in, int fd_out,
     800                 :             :               const char *desc)
     801                 :             : {
     802                 :             :         struct command cmd;
     803                 :             :         char *threads_opt = NULL;
     804                 :             : 
     805                 :             :         command_decompress_init(&cmd, XZ, desc);
     806                 :             : 
     807                 :             :         if (params->threads_max > 0) {
     808                 :             :                 threads_opt = str_fmt("-T%d", params->threads_max);
     809                 :             :                 command_add_arg(&cmd, threads_opt);
     810                 :             :         }
     811                 :             : 
     812                 :             :         fd_fd_filter(&cmd, fd_in, fd_out, env_xz);
     813                 :             : 
     814                 :             :         command_destroy(&cmd);
     815                 :             :         free(threads_opt);
     816                 :             : }
     817                 :             : 
     818                 :             : static void
     819                 :             : compress_xz(struct compress_params *params, int fd_in, int fd_out,
     820                 :             :             const char *desc)
     821                 :             : {
     822                 :             :         struct command cmd;
     823                 :             :         char *threads_opt = NULL;
     824                 :             : 
     825                 :             :         command_compress_init(&cmd, XZ, desc, params->level);
     826                 :             : 
     827                 :             :         if (params->strategy == COMPRESSOR_STRATEGY_EXTREME)
     828                 :             :                 command_add_arg(&cmd, "-e");
     829                 :             : 
     830                 :             :         if (params->threads_max > 0) {
     831                 :             :                 /* Do not generate warnings when adjusting memory usage, nor
     832                 :             :                  * exit with non-zero due to those not emitted warnings. */
     833                 :             :                 command_add_arg(&cmd, "--quiet");
     834                 :             :                 command_add_arg(&cmd, "--no-warn");
     835                 :             : 
     836                 :             :                 /* Do not let xz fallback to single-threaded mode, to avoid
     837                 :             :                  * non-reproducible output. */
     838                 :             :                 command_add_arg(&cmd, "--no-adjust");
     839                 :             : 
     840                 :             :                 /* The xz -T1 option selects a single-threaded mode which
     841                 :             :                  * generates different output than in multi-threaded mode.
     842                 :             :                  * To avoid the non-reproducible output we pass -T+1
     843                 :             :                  * (supported with xz >= 5.4.0) to request multi-threaded
     844                 :             :                  * mode with a single thread. */
     845                 :             :                 if (params->threads_max == 1)
     846                 :             :                         threads_opt = m_strdup("-T+1");
     847                 :             :                 else
     848                 :             :                         threads_opt = str_fmt("-T%d", params->threads_max);
     849                 :             :                 command_add_arg(&cmd, threads_opt);
     850                 :             :         }
     851                 :             : 
     852                 :             :         fd_fd_filter(&cmd, fd_in, fd_out, env_xz);
     853                 :             : 
     854                 :             :         command_destroy(&cmd);
     855                 :             :         free(threads_opt);
     856                 :             : }
     857                 :             : #endif
     858                 :             : 
     859                 :             : static const struct compressor compressor_xz = {
     860                 :             :         .name = "xz",
     861                 :             :         .extension = ".xz",
     862                 :             :         .default_level = 6,
     863                 :             :         .fixup_params = fixup_none_params,
     864                 :             :         .compress = compress_xz,
     865                 :             :         .decompress = decompress_xz,
     866                 :             : };
     867                 :             : 
     868                 :             : /*
     869                 :             :  * Lzma compressor.
     870                 :             :  */
     871                 :             : 
     872                 :             : #ifdef WITH_LIBLZMA
     873                 :             : static void
     874                 :           1 : filter_unlzma_init(struct io_lzma *io, lzma_stream *s)
     875                 :             : {
     876                 :           1 :         uint64_t memlimit = UINT64_MAX;
     877                 :             :         lzma_ret ret;
     878                 :             : 
     879                 :           1 :         io->filter = DPKG_STREAM_DECOMPRESS;
     880                 :             : 
     881                 :           1 :         ret = lzma_alone_decoder(s, memlimit);
     882         [ -  + ]:           1 :         if (ret != LZMA_OK)
     883                 :           0 :                 filter_lzma_error(io, ret);
     884                 :           1 : }
     885                 :             : 
     886                 :             : static void
     887                 :           0 : filter_lzma_init(struct io_lzma *io, lzma_stream *s)
     888                 :             : {
     889                 :             :         uint32_t preset;
     890                 :             :         lzma_options_lzma options;
     891                 :             :         lzma_ret ret;
     892                 :             : 
     893                 :           0 :         io->filter = DPKG_STREAM_COMPRESS;
     894                 :             : 
     895                 :           0 :         preset = io->params->level;
     896         [ #  # ]:           0 :         if (io->params->strategy == COMPRESSOR_STRATEGY_EXTREME)
     897                 :           0 :                 preset |= LZMA_PRESET_EXTREME;
     898         [ #  # ]:           0 :         if (lzma_lzma_preset(&options, preset))
     899                 :           0 :                 filter_lzma_error(io, LZMA_OPTIONS_ERROR);
     900                 :             : 
     901                 :           0 :         ret = lzma_alone_encoder(s, &options);
     902         [ #  # ]:           0 :         if (ret != LZMA_OK)
     903                 :           0 :                 filter_lzma_error(io, ret);
     904                 :           0 : }
     905                 :             : 
     906                 :             : static void
     907                 :           1 : decompress_lzma(struct compress_params *params, int fd_in, int fd_out,
     908                 :             :                 const char *desc)
     909                 :             : {
     910                 :             :         struct io_lzma io;
     911                 :             : 
     912                 :           1 :         io.init = filter_unlzma_init;
     913                 :           1 :         io.code = filter_lzma_code;
     914                 :           1 :         io.done = filter_lzma_done;
     915                 :           1 :         io.desc = desc;
     916                 :           1 :         io.params = params;
     917                 :             : 
     918                 :           1 :         filter_lzma(&io, fd_in, fd_out);
     919                 :           1 : }
     920                 :             : 
     921                 :             : static void
     922                 :           0 : compress_lzma(struct compress_params *params, int fd_in, int fd_out,
     923                 :             :               const char *desc)
     924                 :             : {
     925                 :             :         struct io_lzma io;
     926                 :             : 
     927                 :           0 :         io.init = filter_lzma_init;
     928                 :           0 :         io.code = filter_lzma_code;
     929                 :           0 :         io.done = filter_lzma_done;
     930                 :           0 :         io.desc = desc;
     931                 :           0 :         io.params = params;
     932                 :             : 
     933                 :           0 :         filter_lzma(&io, fd_in, fd_out);
     934                 :           0 : }
     935                 :             : #else
     936                 :             : static void
     937                 :             : decompress_lzma(struct compress_params *params, int fd_in, int fd_out,
     938                 :             :                 const char *desc)
     939                 :             : {
     940                 :             :         struct command cmd;
     941                 :             : 
     942                 :             :         command_decompress_init(&cmd, XZ, desc);
     943                 :             :         command_add_arg(&cmd, "--format=lzma");
     944                 :             : 
     945                 :             :         fd_fd_filter(&cmd, fd_in, fd_out, env_xz);
     946                 :             : 
     947                 :             :         command_destroy(&cmd);
     948                 :             : }
     949                 :             : 
     950                 :             : static void
     951                 :             : compress_lzma(struct compress_params *params, int fd_in, int fd_out,
     952                 :             :               const char *desc)
     953                 :             : {
     954                 :             :         struct command cmd;
     955                 :             : 
     956                 :             :         command_compress_init(&cmd, XZ, desc, params->level);
     957                 :             :         command_add_arg(&cmd, "--format=lzma");
     958                 :             : 
     959                 :             :         fd_fd_filter(&cmd, fd_in, fd_out, env_xz);
     960                 :             : 
     961                 :             :         command_destroy(&cmd);
     962                 :             : }
     963                 :             : #endif
     964                 :             : 
     965                 :             : static const struct compressor compressor_lzma = {
     966                 :             :         .name = "lzma",
     967                 :             :         .extension = ".lzma",
     968                 :             :         .default_level = 6,
     969                 :             :         .fixup_params = fixup_none_params,
     970                 :             :         .compress = compress_lzma,
     971                 :             :         .decompress = decompress_lzma,
     972                 :             : };
     973                 :             : 
     974                 :             : /*
     975                 :             :  * ZStandard compressor.
     976                 :             :  */
     977                 :             : 
     978                 :             : #define ZSTD            "zstd"
     979                 :             : 
     980                 :             : #ifdef WITH_LIBZSTD
     981                 :             : struct io_zstd_stream {
     982                 :             :         enum dpkg_stream_filter filter;
     983                 :             :         enum dpkg_stream_action action;
     984                 :             :         enum dpkg_stream_status status;
     985                 :             : 
     986                 :             :         union {
     987                 :             :                 ZSTD_CCtx *c;
     988                 :             :                 ZSTD_DCtx *d;
     989                 :             :         } ctx;
     990                 :             : 
     991                 :             :         const uint8_t *next_in;
     992                 :             :         size_t avail_in;
     993                 :             :         uint8_t *next_out;
     994                 :             :         size_t avail_out;
     995                 :             : };
     996                 :             : 
     997                 :             : struct io_zstd {
     998                 :             :         const char *desc;
     999                 :             : 
    1000                 :             :         struct compress_params *params;
    1001                 :             : 
    1002                 :             :         void (*init)(struct io_zstd *io, struct io_zstd_stream *s);
    1003                 :             :         void (*code)(struct io_zstd *io, struct io_zstd_stream *s);
    1004                 :             :         void (*done)(struct io_zstd *io, struct io_zstd_stream *s);
    1005                 :             : };
    1006                 :             : 
    1007                 :             : static void DPKG_ATTR_NORET
    1008                 :           0 : filter_zstd_error(struct io_zstd *io, size_t ret)
    1009                 :             : {
    1010                 :           0 :         ohshit(_("%s: zstd error: %s"), io->desc, ZSTD_getErrorName(ret));
    1011                 :             : }
    1012                 :             : 
    1013                 :             : static uint32_t
    1014                 :           2 : filter_zstd_get_cputhreads(struct compress_params *params)
    1015                 :             : {
    1016                 :             :         ZSTD_bounds workers;
    1017                 :           2 :         long threads_max = 1;
    1018                 :             : 
    1019                 :             :         /* The shared library has not been built with multi-threading. */
    1020                 :           2 :         workers = ZSTD_cParam_getBounds(ZSTD_c_nbWorkers);
    1021         [ -  + ]:           2 :         if (workers.upperBound == 0)
    1022                 :           0 :                 return 1;
    1023                 :             : 
    1024                 :             : #ifdef _SC_NPROCESSORS_ONLN
    1025                 :           2 :         threads_max = sysconf(_SC_NPROCESSORS_ONLN);
    1026         [ -  + ]:           2 :         if (threads_max < 0)
    1027                 :           0 :                 return 1;
    1028                 :             : #endif
    1029                 :             : 
    1030         [ -  + ]:           2 :         if (params->threads_max >= 0)
    1031         [ #  # ]:           0 :                 return clamp(params->threads_max, 1, threads_max);
    1032                 :             : 
    1033                 :           2 :         return threads_max;
    1034                 :             : }
    1035                 :             : 
    1036                 :             : static size_t
    1037                 :           6 : filter_zstd_get_buf_in_size(struct io_zstd_stream *s)
    1038                 :             : {
    1039         [ +  + ]:           6 :         if (s->filter == DPKG_STREAM_DECOMPRESS)
    1040                 :           4 :                 return ZSTD_DStreamInSize();
    1041                 :             :         else
    1042                 :           2 :                 return ZSTD_CStreamInSize();
    1043                 :             : }
    1044                 :             : 
    1045                 :             : static size_t
    1046                 :           6 : filter_zstd_get_buf_out_size(struct io_zstd_stream *s)
    1047                 :             : {
    1048         [ +  + ]:           6 :         if (s->filter == DPKG_STREAM_DECOMPRESS)
    1049                 :           4 :                 return ZSTD_DStreamOutSize();
    1050                 :             :         else
    1051                 :           2 :                 return ZSTD_CStreamOutSize();
    1052                 :             : }
    1053                 :             : 
    1054                 :             : static void
    1055                 :           4 : filter_unzstd_init(struct io_zstd *io, struct io_zstd_stream *s)
    1056                 :             : {
    1057                 :           4 :         s->filter = DPKG_STREAM_DECOMPRESS;
    1058                 :           4 :         s->action = DPKG_STREAM_RUN;
    1059                 :           4 :         s->status = DPKG_STREAM_OK;
    1060                 :             : 
    1061                 :           4 :         s->ctx.d = ZSTD_createDCtx();
    1062         [ -  + ]:           4 :         if (s->ctx.d == NULL)
    1063                 :           0 :                 ohshit(_("%s: cannot create zstd decompression context"),
    1064                 :             :                        io->desc);
    1065                 :           4 : }
    1066                 :             : 
    1067                 :             : static void
    1068                 :          16 : filter_unzstd_code(struct io_zstd *io, struct io_zstd_stream *s)
    1069                 :             : {
    1070                 :          16 :         ZSTD_inBuffer buf_in = { s->next_in, s->avail_in, 0 };
    1071                 :          16 :         ZSTD_outBuffer buf_out = { s->next_out, s->avail_out, 0 };
    1072                 :             :         size_t ret;
    1073                 :             : 
    1074                 :          16 :         ret = ZSTD_decompressStream(s->ctx.d, &buf_out, &buf_in);
    1075         [ -  + ]:          16 :         if (ZSTD_isError(ret))
    1076                 :           0 :                 filter_zstd_error(io, ret);
    1077                 :             : 
    1078                 :          16 :         s->next_in += buf_in.pos;
    1079                 :          16 :         s->avail_in -= buf_in.pos;
    1080                 :          16 :         s->next_out += buf_out.pos;
    1081                 :          16 :         s->avail_out -= buf_out.pos;
    1082                 :             : 
    1083         [ +  + ]:          16 :         if (ret == 0)
    1084                 :           4 :                 s->status = DPKG_STREAM_END;
    1085                 :          16 : }
    1086                 :             : 
    1087                 :             : static void
    1088                 :           4 : filter_unzstd_done(struct io_zstd *io, struct io_zstd_stream *s)
    1089                 :             : {
    1090                 :           4 :         ZSTD_freeDCtx(s->ctx.d);
    1091                 :           4 : }
    1092                 :             : 
    1093                 :             : static void
    1094                 :           2 : filter_zstd_init(struct io_zstd *io, struct io_zstd_stream *s)
    1095                 :             : {
    1096                 :           2 :         int clevel = io->params->level;
    1097                 :             :         uint32_t nthreads;
    1098                 :             :         size_t ret;
    1099                 :             : 
    1100                 :           2 :         s->filter = DPKG_STREAM_COMPRESS;
    1101                 :           2 :         s->action = DPKG_STREAM_RUN;
    1102                 :           2 :         s->status = DPKG_STREAM_OK;
    1103                 :             : 
    1104                 :           2 :         s->ctx.c = ZSTD_createCCtx();
    1105         [ -  + ]:           2 :         if (s->ctx.c == NULL)
    1106                 :           0 :                 ohshit(_("%s: cannot create zstd compression context"),
    1107                 :             :                        io->desc);
    1108                 :             : 
    1109                 :           2 :         ret = ZSTD_CCtx_setParameter(s->ctx.c, ZSTD_c_compressionLevel, clevel);
    1110         [ -  + ]:           2 :         if (ZSTD_isError(ret))
    1111                 :           0 :                 filter_zstd_error(io, ret);
    1112                 :           2 :         ret = ZSTD_CCtx_setParameter(s->ctx.c, ZSTD_c_checksumFlag, 1);
    1113         [ -  + ]:           2 :         if (ZSTD_isError(ret))
    1114                 :           0 :                 filter_zstd_error(io, ret);
    1115                 :             : 
    1116                 :           2 :         nthreads = filter_zstd_get_cputhreads(io->params);
    1117         [ +  - ]:           2 :         if (nthreads > 1)
    1118                 :           2 :                 ZSTD_CCtx_setParameter(s->ctx.c, ZSTD_c_nbWorkers, nthreads);
    1119                 :           2 : }
    1120                 :             : 
    1121                 :             : static void
    1122                 :           7 : filter_zstd_code(struct io_zstd *io, struct io_zstd_stream *s)
    1123                 :             : {
    1124                 :           7 :         ZSTD_inBuffer buf_in = { s->next_in, s->avail_in, 0 };
    1125                 :           7 :         ZSTD_outBuffer buf_out = { s->next_out, s->avail_out, 0 };
    1126                 :             :         ZSTD_EndDirective action;
    1127                 :             :         size_t ret;
    1128                 :             : 
    1129         [ +  + ]:           7 :         if (s->action == DPKG_STREAM_FINISH)
    1130                 :           3 :                 action = ZSTD_e_end;
    1131                 :             :         else
    1132                 :           4 :                 action = ZSTD_e_continue;
    1133                 :             : 
    1134                 :           7 :         ret = ZSTD_compressStream2(s->ctx.c, &buf_out, &buf_in, action);
    1135         [ -  + ]:           7 :         if (ZSTD_isError(ret))
    1136                 :           0 :                 filter_zstd_error(io, ret);
    1137                 :             : 
    1138                 :           7 :         s->next_in += buf_in.pos;
    1139                 :           7 :         s->avail_in -= buf_in.pos;
    1140                 :           7 :         s->next_out += buf_out.pos;
    1141                 :           7 :         s->avail_out -= buf_out.pos;
    1142                 :             : 
    1143   [ +  +  +  + ]:           7 :         if (s->action == DPKG_STREAM_FINISH && ret == 0)
    1144                 :           2 :                 s->status = DPKG_STREAM_END;
    1145                 :           7 : }
    1146                 :             : 
    1147                 :             : static void
    1148                 :           2 : filter_zstd_done(struct io_zstd *io, struct io_zstd_stream *s)
    1149                 :             : {
    1150                 :           2 :         ZSTD_freeCCtx(s->ctx.c);
    1151                 :           2 : }
    1152                 :             : 
    1153                 :             : static void
    1154                 :           6 : filter_zstd(struct io_zstd *io, int fd_in, int fd_out)
    1155                 :             : {
    1156                 :             :         ssize_t buf_in_size;
    1157                 :             :         ssize_t buf_out_size;
    1158                 :             :         uint8_t *buf_in;
    1159                 :             :         uint8_t *buf_out;
    1160                 :           6 :         struct io_zstd_stream s = {
    1161                 :             :                 .action = DPKG_STREAM_INIT,
    1162                 :             :         };
    1163                 :             : 
    1164                 :           6 :         io->init(io, &s);
    1165                 :             : 
    1166                 :           6 :         buf_in_size = filter_zstd_get_buf_in_size(&s);
    1167                 :           6 :         buf_in = m_malloc(buf_in_size);
    1168                 :           6 :         buf_out_size = filter_zstd_get_buf_out_size(&s);
    1169                 :           6 :         buf_out = m_malloc(buf_out_size);
    1170                 :             : 
    1171                 :           6 :         s.next_out = buf_out;
    1172                 :           6 :         s.avail_out = buf_out_size;
    1173                 :             : 
    1174                 :             :         do {
    1175                 :             :                 ssize_t len;
    1176                 :             : 
    1177   [ +  +  +  + ]:          23 :                 if (s.avail_in == 0 && s.action != DPKG_STREAM_FINISH) {
    1178                 :          13 :                         len = fd_read(fd_in, buf_in, buf_in_size);
    1179         [ -  + ]:          13 :                         if (len < 0)
    1180                 :           0 :                                 ohshite(_("%s: zstd read error"), io->desc);
    1181         [ +  + ]:          13 :                         if (len < buf_in_size)
    1182                 :           6 :                                 s.action = DPKG_STREAM_FINISH;
    1183                 :          13 :                         s.next_in = buf_in;
    1184                 :          13 :                         s.avail_in = len;
    1185                 :             :                 }
    1186                 :             : 
    1187                 :          23 :                 io->code(io, &s);
    1188                 :             : 
    1189   [ +  +  +  + ]:          23 :                 if (s.avail_out == 0 || s.status == DPKG_STREAM_END) {
    1190                 :          19 :                         len = fd_write(fd_out, buf_out, s.next_out - buf_out);
    1191         [ -  + ]:          19 :                         if (len < 0)
    1192                 :           0 :                                 ohshite(_("%s: zstd write error"), io->desc);
    1193                 :          19 :                         s.next_out = buf_out;
    1194                 :          19 :                         s.avail_out = buf_out_size;
    1195                 :             :                 }
    1196         [ +  + ]:          23 :         } while (s.status != DPKG_STREAM_END);
    1197                 :             : 
    1198                 :           6 :         io->done(io, &s);
    1199                 :             : 
    1200                 :           6 :         free(buf_in);
    1201                 :           6 :         free(buf_out);
    1202                 :             : 
    1203         [ -  + ]:           6 :         if (close(fd_out))
    1204                 :           0 :                 ohshite(_("%s: zstd close error"), io->desc);
    1205                 :           6 : }
    1206                 :             : 
    1207                 :             : static void
    1208                 :           4 : decompress_zstd(struct compress_params *params, int fd_in, int fd_out,
    1209                 :             :                 const char *desc)
    1210                 :             : {
    1211                 :             :         struct io_zstd io;
    1212                 :             : 
    1213                 :           4 :         io.init = filter_unzstd_init;
    1214                 :           4 :         io.code = filter_unzstd_code;
    1215                 :           4 :         io.done = filter_unzstd_done;
    1216                 :           4 :         io.desc = desc;
    1217                 :           4 :         io.params = params;
    1218                 :             : 
    1219                 :           4 :         filter_zstd(&io, fd_in, fd_out);
    1220                 :           4 : }
    1221                 :             : 
    1222                 :             : static void
    1223                 :           2 : compress_zstd(struct compress_params *params, int fd_in, int fd_out,
    1224                 :             :               const char *desc)
    1225                 :             : {
    1226                 :             :         struct io_zstd io;
    1227                 :             : 
    1228                 :           2 :         io.init = filter_zstd_init;
    1229                 :           2 :         io.code = filter_zstd_code;
    1230                 :           2 :         io.done = filter_zstd_done;
    1231                 :           2 :         io.desc = desc;
    1232                 :           2 :         io.params = params;
    1233                 :             : 
    1234                 :           2 :         filter_zstd(&io, fd_in, fd_out);
    1235                 :           2 : }
    1236                 :             : #else
    1237                 :             : static const char *env_zstd[] = {
    1238                 :             :         "ZSTD_CLEVEL",
    1239                 :             :         "ZSTD_NBTHREADS",
    1240                 :             :         NULL,
    1241                 :             : };
    1242                 :             : 
    1243                 :             : static void
    1244                 :             : decompress_zstd(struct compress_params *params, int fd_in, int fd_out,
    1245                 :             :                 const char *desc)
    1246                 :             : {
    1247                 :             :         struct command cmd;
    1248                 :             :         char *threads_opt = NULL;
    1249                 :             : 
    1250                 :             :         command_decompress_init(&cmd, ZSTD, desc);
    1251                 :             :         command_add_arg(&cmd, "-q");
    1252                 :             : 
    1253                 :             :         if (params->threads_max > 0) {
    1254                 :             :                 threads_opt = str_fmt("-T%d", params->threads_max);
    1255                 :             :                 command_add_arg(&cmd, threads_opt);
    1256                 :             :         }
    1257                 :             : 
    1258                 :             :         fd_fd_filter(&cmd, fd_in, fd_out, env_zstd);
    1259                 :             : 
    1260                 :             :         command_destroy(&cmd);
    1261                 :             :         free(threads_opt);
    1262                 :             : }
    1263                 :             : 
    1264                 :             : static void
    1265                 :             : compress_zstd(struct compress_params *params, int fd_in, int fd_out,
    1266                 :             :               const char *desc)
    1267                 :             : {
    1268                 :             :         struct command cmd;
    1269                 :             :         char *threads_opt = NULL;
    1270                 :             : 
    1271                 :             :         command_compress_init(&cmd, ZSTD, desc, params->level);
    1272                 :             :         command_add_arg(&cmd, "-q");
    1273                 :             : 
    1274                 :             :         if (params->level > 19)
    1275                 :             :                 command_add_arg(&cmd, "--ultra");
    1276                 :             : 
    1277                 :             :         if (params->threads_max > 0) {
    1278                 :             :                 threads_opt = str_fmt("-T%d", params->threads_max);
    1279                 :             :                 command_add_arg(&cmd, threads_opt);
    1280                 :             :         }
    1281                 :             : 
    1282                 :             :         fd_fd_filter(&cmd, fd_in, fd_out, env_zstd);
    1283                 :             : 
    1284                 :             :         command_destroy(&cmd);
    1285                 :             :         free(threads_opt);
    1286                 :             : }
    1287                 :             : #endif
    1288                 :             : 
    1289                 :             : static const struct compressor compressor_zstd = {
    1290                 :             :         .name = "zstd",
    1291                 :             :         .extension = ".zst",
    1292                 :             :         .default_level = DPKG_ZSTD_CLEVEL_DEFAULT,
    1293                 :             :         .fixup_params = fixup_none_params,
    1294                 :             :         .compress = compress_zstd,
    1295                 :             :         .decompress = decompress_zstd,
    1296                 :             : };
    1297                 :             : 
    1298                 :             : /*
    1299                 :             :  * Generic compressor filter.
    1300                 :             :  */
    1301                 :             : 
    1302                 :             : static const struct compressor *compressor_array[] = {
    1303                 :             :         [COMPRESSOR_TYPE_NONE] = &compressor_none,
    1304                 :             :         [COMPRESSOR_TYPE_GZIP] = &compressor_gzip,
    1305                 :             :         [COMPRESSOR_TYPE_XZ] = &compressor_xz,
    1306                 :             :         [COMPRESSOR_TYPE_ZSTD] = &compressor_zstd,
    1307                 :             :         [COMPRESSOR_TYPE_BZIP2] = &compressor_bzip2,
    1308                 :             :         [COMPRESSOR_TYPE_LZMA] = &compressor_lzma,
    1309                 :             : };
    1310                 :             : 
    1311                 :             : static const struct compressor *
    1312                 :         262 : compressor(enum compressor_type type)
    1313                 :             : {
    1314                 :         262 :         const enum compressor_type max_type = array_count(compressor_array);
    1315                 :             : 
    1316   [ +  -  -  + ]:         262 :         if (type < 0 || type >= max_type)
    1317                 :           0 :                 internerr("compressor_type %d is out of range", type);
    1318                 :             : 
    1319                 :         262 :         return compressor_array[type];
    1320                 :             : }
    1321                 :             : 
    1322                 :             : const char *
    1323                 :           0 : compressor_get_name(enum compressor_type type)
    1324                 :             : {
    1325                 :           0 :         return compressor(type)->name;
    1326                 :             : }
    1327                 :             : 
    1328                 :             : const char *
    1329                 :          33 : compressor_get_extension(enum compressor_type type)
    1330                 :             : {
    1331                 :          33 :         return compressor(type)->extension;
    1332                 :             : }
    1333                 :             : 
    1334                 :             : enum compressor_type
    1335                 :          10 : compressor_find_by_name(const char *name)
    1336                 :             : {
    1337                 :             :         size_t i;
    1338                 :             : 
    1339         [ +  - ]:          16 :         for (i = 0; i < array_count(compressor_array); i++)
    1340         [ +  + ]:          16 :                 if (strcmp(compressor_array[i]->name, name) == 0)
    1341                 :          10 :                         return i;
    1342                 :             : 
    1343                 :           0 :         return COMPRESSOR_TYPE_UNKNOWN;
    1344                 :             : }
    1345                 :             : 
    1346                 :             : enum compressor_type
    1347                 :          46 : compressor_find_by_extension(const char *extension)
    1348                 :             : {
    1349                 :             :         size_t i;
    1350                 :             : 
    1351         [ +  + ]:         109 :         for (i = 0; i < array_count(compressor_array); i++)
    1352         [ +  + ]:         108 :                 if (strcmp(compressor_array[i]->extension, extension) == 0)
    1353                 :          45 :                         return i;
    1354                 :             : 
    1355                 :           1 :         return COMPRESSOR_TYPE_UNKNOWN;
    1356                 :             : }
    1357                 :             : 
    1358                 :             : enum compressor_strategy
    1359                 :           0 : compressor_get_strategy(const char *name)
    1360                 :             : {
    1361         [ #  # ]:           0 :         if (strcmp(name, "none") == 0)
    1362                 :           0 :                 return COMPRESSOR_STRATEGY_NONE;
    1363         [ #  # ]:           0 :         if (strcmp(name, "filtered") == 0)
    1364                 :           0 :                 return COMPRESSOR_STRATEGY_FILTERED;
    1365         [ #  # ]:           0 :         if (strcmp(name, "huffman") == 0)
    1366                 :           0 :                 return COMPRESSOR_STRATEGY_HUFFMAN;
    1367         [ #  # ]:           0 :         if (strcmp(name, "rle") == 0)
    1368                 :           0 :                 return COMPRESSOR_STRATEGY_RLE;
    1369         [ #  # ]:           0 :         if (strcmp(name, "fixed") == 0)
    1370                 :           0 :                 return COMPRESSOR_STRATEGY_FIXED;
    1371         [ #  # ]:           0 :         if (strcmp(name, "extreme") == 0)
    1372                 :           0 :                 return COMPRESSOR_STRATEGY_EXTREME;
    1373                 :             : 
    1374                 :           0 :         return COMPRESSOR_STRATEGY_UNKNOWN;
    1375                 :             : }
    1376                 :             : 
    1377                 :             : static void
    1378                 :          78 : compressor_fixup_params(struct compress_params *params)
    1379                 :             : {
    1380                 :          78 :         compressor(params->type)->fixup_params(params);
    1381                 :             : 
    1382         [ +  - ]:          78 :         if (params->level < 0)
    1383                 :          78 :                 params->level = compressor(params->type)->default_level;
    1384                 :          78 : }
    1385                 :             : 
    1386                 :             : bool
    1387                 :          78 : compressor_check_params(struct compress_params *params, struct dpkg_error *err)
    1388                 :             : {
    1389                 :          78 :         compressor_fixup_params(params);
    1390                 :             : 
    1391   [ +  +  +  - ]:          79 :         if ((params->type == COMPRESSOR_TYPE_ZSTD &&
    1392                 :           1 :              params->level > DPKG_ZSTD_MAX_LEVEL) ||
    1393         [ +  + ]:          78 :             (params->type != COMPRESSOR_TYPE_ZSTD &&
    1394         [ -  + ]:          77 :              params->level > 9)) {
    1395                 :           0 :                 dpkg_put_error(err, _("invalid compression level %d"),
    1396                 :             :                                params->level);
    1397                 :           0 :                 return false;
    1398                 :             :         }
    1399                 :             : 
    1400         [ +  - ]:          78 :         if (params->strategy == COMPRESSOR_STRATEGY_NONE)
    1401                 :          78 :                 return true;
    1402                 :             : 
    1403         [ #  # ]:           0 :         if (params->type == COMPRESSOR_TYPE_GZIP &&
    1404         [ #  # ]:           0 :             (params->strategy == COMPRESSOR_STRATEGY_FILTERED ||
    1405         [ #  # ]:           0 :              params->strategy == COMPRESSOR_STRATEGY_HUFFMAN ||
    1406         [ #  # ]:           0 :              params->strategy == COMPRESSOR_STRATEGY_RLE ||
    1407         [ #  # ]:           0 :              params->strategy == COMPRESSOR_STRATEGY_FIXED))
    1408                 :           0 :                 return true;
    1409                 :             : 
    1410         [ #  # ]:           0 :         if (params->type == COMPRESSOR_TYPE_XZ &&
    1411         [ #  # ]:           0 :             params->strategy == COMPRESSOR_STRATEGY_EXTREME)
    1412                 :           0 :                 return true;
    1413                 :             : 
    1414                 :           0 :         dpkg_put_error(err, _("unknown compression strategy"));
    1415                 :           0 :         return false;
    1416                 :             : }
    1417                 :             : 
    1418                 :             : void
    1419                 :          37 : decompress_filter(struct compress_params *params, int fd_in, int fd_out,
    1420                 :             :                   const char *desc_fmt, ...)
    1421                 :             : {
    1422                 :             :         va_list args;
    1423                 :          37 :         struct varbuf desc = VARBUF_INIT;
    1424                 :             : 
    1425                 :          37 :         va_start(args, desc_fmt);
    1426                 :          37 :         varbuf_vprintf(&desc, desc_fmt, args);
    1427                 :          37 :         va_end(args);
    1428                 :             : 
    1429                 :          37 :         compressor(params->type)->decompress(params, fd_in, fd_out, desc.buf);
    1430                 :             : 
    1431                 :          37 :         varbuf_destroy(&desc);
    1432                 :          37 : }
    1433                 :             : 
    1434                 :             : void
    1435                 :          36 : compress_filter(struct compress_params *params, int fd_in, int fd_out,
    1436                 :             :                 const char *desc_fmt, ...)
    1437                 :             : {
    1438                 :             :         va_list args;
    1439                 :          36 :         struct varbuf desc = VARBUF_INIT;
    1440                 :             : 
    1441                 :          36 :         va_start(args, desc_fmt);
    1442                 :          36 :         varbuf_vprintf(&desc, desc_fmt, args);
    1443                 :          36 :         va_end(args);
    1444                 :             : 
    1445                 :          36 :         compressor(params->type)->compress(params, fd_in, fd_out, desc.buf);
    1446                 :             : 
    1447                 :          36 :         varbuf_destroy(&desc);
    1448                 :          36 : }
        

Generated by: LCOV version 2.0-1