Coverage Report

Created: 2024-05-20 06:23

/src/mupdf/source/fitz/directory.c
Line
Count
Source (jump to first uncovered line)
1
// Copyright (C) 2004-2021 Artifex Software, Inc.
2
//
3
// This file is part of MuPDF.
4
//
5
// MuPDF is free software: you can redistribute it and/or modify it under the
6
// terms of the GNU Affero General Public License as published by the Free
7
// Software Foundation, either version 3 of the License, or (at your option)
8
// any later version.
9
//
10
// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
// details.
14
//
15
// You should have received a copy of the GNU Affero General Public License
16
// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17
//
18
// Alternative licensing terms are available from the licensor.
19
// For commercial licensing, see <https://www.artifex.com/> or contact
20
// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21
// CA 94129, USA, for further information.
22
23
#include "mupdf/fitz.h"
24
25
#include <string.h>
26
#include <errno.h>
27
#include <sys/stat.h>
28
29
#ifdef _MSC_VER
30
#include <windows.h>
31
#include <errno.h>
32
#define stat _stat
33
#else
34
#include <dirent.h>
35
#endif
36
37
typedef struct
38
{
39
  fz_archive super;
40
41
  char *path;
42
43
  int max_entries;
44
  int num_entries;
45
  char **entries;
46
} fz_directory;
47
48
static void drop_directory(fz_context *ctx, fz_archive *arch)
49
0
{
50
0
  fz_directory *dir = (fz_directory *) arch;
51
0
  int i;
52
53
0
  fz_free(ctx, dir->path);
54
0
  for (i = 0; i < dir->num_entries; i++)
55
0
    fz_free(ctx, dir->entries[i]);
56
0
  fz_free(ctx, dir->entries);
57
0
}
58
59
static void make_dir_path(char *output, fz_archive *arch, const char *tail, size_t size)
60
0
{
61
  /* Skip any leading ../ path segments, so we don't look outside the
62
   * directory itself. The paths coming here have already been
63
   * canonicalized with fz_cleanname so any remaining ".." parts are
64
   * guaranteed to be at the start of the path.
65
   */
66
0
  fz_directory *dir = (fz_directory *) arch;
67
0
  while (tail[0] == '.' && tail[1] == '.' && tail[2] == '/')
68
0
    tail += 3;
69
0
  fz_strlcpy(output, dir->path, size);
70
0
  fz_strlcat(output, "/", size);
71
0
  fz_strlcat(output, tail, size);
72
0
}
73
74
static fz_stream *open_dir_entry(fz_context *ctx, fz_archive *arch, const char *name)
75
0
{
76
0
  char path[PATH_MAX];
77
0
  make_dir_path(path, arch, name, sizeof path);
78
0
  return fz_try_open_file(ctx, path);
79
0
}
80
81
static fz_buffer *read_dir_entry(fz_context *ctx, fz_archive *arch, const char *name)
82
0
{
83
0
  char path[PATH_MAX];
84
0
  make_dir_path(path, arch, name, sizeof path);
85
0
  return fz_try_read_file(ctx, path);
86
0
}
87
88
static int has_dir_entry(fz_context *ctx, fz_archive *arch, const char *name)
89
0
{
90
0
  char path[PATH_MAX];
91
0
  make_dir_path(path, arch, name, sizeof path);
92
0
  return fz_file_exists(ctx, path);
93
0
}
94
95
int
96
fz_is_directory(fz_context *ctx, const char *path)
97
0
{
98
0
  struct stat info;
99
100
0
  if (stat(path, &info) < 0)
101
0
    return 0;
102
103
0
  return S_ISDIR(info.st_mode);
104
0
}
105
106
static int
107
count_dir_entries(fz_context *ctx, fz_archive *arch)
108
0
{
109
0
  fz_directory *dir = (fz_directory *) arch;
110
111
0
  return dir->num_entries;
112
0
}
113
114
const char *
115
list_dir_entry(fz_context *ctx, fz_archive *arch, int n)
116
0
{
117
0
  fz_directory *dir = (fz_directory *) arch;
118
119
0
  if (n < 0 || n >= dir->num_entries)
120
0
    return NULL;
121
122
0
  return dir->entries[n];
123
0
}
124
125
fz_archive *
126
fz_open_directory(fz_context *ctx, const char *path)
127
0
{
128
0
  fz_directory *dir;
129
#ifdef _MSC_VER
130
  WCHAR *wpath = NULL;
131
  size_t z = 3;
132
  HANDLE h = NULL;
133
  WIN32_FIND_DATAW dw;
134
135
  fz_var(wpath);
136
  fz_var(h);
137
#else
138
0
  DIR *dp;
139
0
  struct dirent *ep;
140
141
0
  fz_var(dp);
142
0
#endif
143
144
0
  if (!fz_is_directory(ctx, path))
145
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "'%s' is not a directory", path);
146
147
0
  dir = fz_new_derived_archive(ctx, NULL, fz_directory);
148
0
  dir->super.format = "dir";
149
0
  dir->super.count_entries = count_dir_entries;
150
0
  dir->super.list_entry = list_dir_entry;
151
0
  dir->super.has_entry = has_dir_entry;
152
0
  dir->super.read_entry = read_dir_entry;
153
0
  dir->super.open_entry = open_dir_entry;
154
0
  dir->super.drop_archive = drop_directory;
155
156
0
  fz_try(ctx)
157
0
  {
158
#ifdef _MSC_VER
159
    char const *p = path;
160
    WCHAR *w;
161
    while (*p)
162
    {
163
      int rune;
164
      p += fz_chartorune(&rune, p);
165
      if (rune >= 0x10000 || rune < 0)
166
      {
167
        errno = EINVAL;
168
        fz_throw(ctx, FZ_ERROR_SYSTEM, "Unrepresentable UTF-8 char in directory name");
169
      }
170
      z++;
171
    }
172
    w = wpath = fz_malloc(ctx, z * sizeof(WCHAR));
173
    p = path;
174
    while (*p)
175
    {
176
      int rune;
177
      p += fz_chartorune(&rune, p);
178
      *w++ = rune;
179
    }
180
    w[0] = '\\';
181
    w[1] = '*';
182
    w[2] = 0;
183
184
    /* Now enumerate the paths. */
185
    h = FindFirstFileW(wpath, &dw);
186
    if (h == INVALID_HANDLE_VALUE)
187
      break;
188
189
    do
190
    {
191
      char *u;
192
193
      if (dir->max_entries == dir->num_entries)
194
      {
195
        int newmax = dir->max_entries * 2;
196
        if (newmax == 0)
197
          newmax = 32;
198
199
        dir->entries = fz_realloc(ctx, dir->entries, sizeof(*dir->entries) * newmax);
200
        dir->max_entries = newmax;
201
      }
202
203
      /* Count the len as utf-8. */
204
      w = dw.cFileName;
205
      z = 1;
206
      while (*w)
207
        z += fz_runelen(*w++);
208
209
      u = dir->entries[dir->num_entries] = fz_malloc(ctx, z);
210
      dir->num_entries++;
211
212
      /* Copy the name across. */
213
      w = dw.cFileName;
214
      while (*w)
215
        u += fz_runetochar(u, *w++);
216
      *u = 0;
217
    }
218
    while (FindNextFileW(h, &dw));
219
#else
220
0
    dp = opendir(path);
221
0
    if (dp == NULL)
222
0
      break;
223
224
0
    while ((ep = readdir(dp)) != NULL)
225
0
    {
226
0
      if (dir->max_entries == dir->num_entries)
227
0
      {
228
0
        int newmax = dir->max_entries * 2;
229
0
        if (newmax == 0)
230
0
          newmax = 32;
231
232
0
        dir->entries = fz_realloc(ctx, dir->entries, sizeof(*dir->entries) * newmax);
233
0
        dir->max_entries = newmax;
234
0
      }
235
236
0
      dir->entries[dir->num_entries] = fz_strdup(ctx, ep->d_name);
237
0
      dir->num_entries++;
238
0
    }
239
0
#endif
240
0
    dir->path = fz_strdup(ctx, path);
241
0
  }
242
0
  fz_always(ctx)
243
0
  {
244
#ifdef _MSC_VER
245
    fz_free(ctx, wpath);
246
    if (h)
247
      (void)FindClose(h);
248
#else
249
0
    if (dp)
250
0
      (void)closedir(dp);
251
0
#endif
252
0
  }
253
0
  fz_catch(ctx)
254
0
  {
255
0
    fz_drop_archive(ctx, &dir->super);
256
0
    fz_rethrow(ctx);
257
0
  }
258
259
0
  return &dir->super;
260
0
}