source: mainline/uspace/app/bdsh/cmds/modules/ls/ls.c@ ab936440

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since ab936440 was 59cce22, checked in by Jakub Jermář <jakub@…>, 7 years ago

Prevents 'ls -r' from crashing when executed in an empty directory

This fixes #779 by adding an extra test around realloc().
The current implementation of realloc() releases the given pointer
and returns NULL if the new requested size is 0. This behavior is
implementation-defined and has been changed with d802331. The
function ls_scan_dir() expected NULL to be returned only in
case of an allocation failure. Therefore, the function
ls_scan_dir() used to display an CL_ENOMEM error and returned
back to ls_recursive(). ls_recursive() would attempt to release
the already freed pointer a second time which would lead
bdsh to break. This commit will prevent this from happening by
avoiding the realloc() call in the first place.

  • Property mode set to 100644
File size: 10.2 KB
Line 
1/*
2 * Copyright (c) 2008 Tim Post
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/*
30 * NOTE:
31 * This is a bit of an ugly hack, working around the absence of fstat / etc.
32 * As more stuff is completed and exposed in libc, this will improve
33 */
34
35#include <errno.h>
36#include <str_error.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <stdint.h>
40#include <dirent.h>
41#include <getopt.h>
42#include <vfs/vfs.h>
43#include <str.h>
44
45#include "ls.h"
46#include "errors.h"
47#include "config.h"
48#include "util.h"
49#include "entry.h"
50#include "cmds.h"
51
52static const char *cmdname = "ls";
53
54static ls_job_t ls;
55
56static struct option const long_options[] = {
57 { "help", no_argument, 0, 'h' },
58 { "unsort", no_argument, 0, 'u' },
59 { "recursive", no_argument, 0, 'r' },
60 { 0, 0, 0, 0 }
61};
62
63/* Prototypes for the ls command, excluding entry points. */
64static unsigned int ls_start(ls_job_t *);
65static void ls_print(struct dir_elem_t *);
66static int ls_cmp(const void *, const void *);
67static signed int ls_scan_dir(const char *, DIR *, struct dir_elem_t **);
68static unsigned int ls_recursive(const char *, DIR *);
69static unsigned int ls_scope(const char *, struct dir_elem_t *);
70
71static unsigned int ls_start(ls_job_t *ls)
72{
73 ls->recursive = 0;
74 ls->sort = 1;
75
76 return 1;
77}
78
79/** Print an entry.
80 *
81 * ls_print currently does nothing more than print the entry.
82 * In the future, we will likely pass the absolute path, and
83 * some sort of ls_options structure that controls how each
84 * entry is printed and what is printed about it.
85 *
86 * Now we just print basic DOS style lists.
87 *
88 * @param de Directory element.
89 */
90static void ls_print(struct dir_elem_t *de)
91{
92 if (de->s.is_file)
93 printf("%-40s\t%llu\n", de->name, (long long) de->s.size);
94 else if (de->s.is_directory)
95 printf("%-40s\t<dir>\n", de->name);
96 else
97 printf("%-40s\n", de->name);
98}
99
100/** Compare 2 directory elements.
101 *
102 * It compares 2 elements of a directory : a file is considered
103 * as bigger than a directory, and if they have the same type,
104 * they are compared alphabetically.
105 *
106 * @param a Pointer to the structure of the first element.
107 * @param b Pointer to the structure of the second element.
108 *
109 * @return -1 if a < b, 1 otherwise.
110 */
111static int ls_cmp(const void *a, const void *b)
112{
113 struct dir_elem_t const *da = a;
114 struct dir_elem_t const *db = b;
115
116 if ((da->s.is_directory && db->s.is_file) ||
117 ((da->s.is_directory == db->s.is_directory) &&
118 str_cmp(da->name, db->name) < 0))
119 return -1;
120 else
121 return 1;
122}
123
124/** Scan a directory.
125 *
126 * Scan the content of a directory and print it.
127 *
128 * @param d Name of the directory.
129 * @param dirp Directory stream.
130 * @param sort 1 if the output must be sorted,
131 * 0 otherwise.
132 */
133static signed int ls_scan_dir(const char *d, DIR *dirp,
134 struct dir_elem_t **dir_list_ptr)
135{
136 int alloc_blocks = 20;
137 int i;
138 int nbdirs = 0;
139 errno_t rc;
140 int len;
141 char *buff;
142 struct dir_elem_t *tmp;
143 struct dir_elem_t *tosort;
144 struct dirent *dp;
145
146 if (!dirp)
147 return -1;
148
149 buff = (char *) malloc(PATH_MAX);
150 if (!buff) {
151 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
152 return -1;
153 }
154
155 tosort = (struct dir_elem_t *) malloc(alloc_blocks * sizeof(*tosort));
156 if (!tosort) {
157 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
158 free(buff);
159 return -1;
160 }
161
162 while ((dp = readdir(dirp))) {
163 if (nbdirs + 1 > alloc_blocks) {
164 alloc_blocks += alloc_blocks;
165
166 tmp = (struct dir_elem_t *) realloc(tosort,
167 alloc_blocks * sizeof(struct dir_elem_t));
168 if (!tmp) {
169 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
170 goto out;
171 }
172 tosort = tmp;
173 }
174
175 /* fill the name field */
176 tosort[nbdirs].name = (char *) malloc(str_size(dp->d_name) + 1);
177 if (!tosort[nbdirs].name) {
178 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
179 goto out;
180 }
181
182 str_cpy(tosort[nbdirs].name, str_size(dp->d_name) + 1, dp->d_name);
183 len = snprintf(buff, PATH_MAX - 1, "%s/%s", d, tosort[nbdirs].name);
184 buff[len] = '\0';
185
186 rc = vfs_stat_path(buff, &tosort[nbdirs++].s);
187 if (rc != EOK) {
188 printf("ls: skipping bogus node %s\n", buff);
189 printf("error=%s\n", str_error_name(rc));
190 goto out;
191 }
192 }
193
194 if (ls.sort)
195 qsort(&tosort[0], nbdirs, sizeof(struct dir_elem_t), ls_cmp);
196
197 for (i = 0; i < nbdirs; i++)
198 ls_print(&tosort[i]);
199
200 /* Populate the directory list. */
201 if (ls.recursive && nbdirs > 0) {
202 tmp = (struct dir_elem_t *) realloc(*dir_list_ptr,
203 nbdirs * sizeof(struct dir_elem_t));
204 if (!tmp) {
205 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
206 goto out;
207 }
208 *dir_list_ptr = tmp;
209
210 for (i = 0; i < nbdirs; i++) {
211 (*dir_list_ptr)[i].name = str_dup(tosort[i].name);
212 if (!(*dir_list_ptr)[i].name) {
213 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
214 goto out;
215 }
216 }
217 }
218
219out:
220 for (i = 0; i < nbdirs; i++)
221 free(tosort[i].name);
222 free(tosort);
223 free(buff);
224
225 return nbdirs;
226}
227
228/** Visit a directory recursively.
229 *
230 * ls_recursive visits all the subdirectories recursively and
231 * prints the files and directories in them.
232 *
233 * @param path Path the current directory being visited.
234 * @param dirp Directory stream.
235 */
236static unsigned int ls_recursive(const char *path, DIR *dirp)
237{
238 int i, nbdirs, ret;
239 unsigned int scope;
240 char *subdir_path;
241 DIR *subdirp;
242 struct dir_elem_t *dir_list;
243
244 const char *const trailing_slash = "/";
245
246 nbdirs = 0;
247 dir_list = (struct dir_elem_t *) malloc(sizeof(struct dir_elem_t));
248
249 printf("\n%s:\n", path);
250
251 subdir_path = (char *) malloc(PATH_MAX);
252 if (!subdir_path) {
253 ret = CMD_FAILURE;
254 goto out;
255 }
256
257 nbdirs = ls_scan_dir(path, dirp, &dir_list);
258 if (nbdirs == -1) {
259 ret = CMD_FAILURE;
260 goto out;
261 }
262
263 for (i = 0; i < nbdirs; ++i) {
264 memset(subdir_path, 0, PATH_MAX);
265
266 if (str_size(subdir_path) + str_size(path) + 1 <= PATH_MAX)
267 str_append(subdir_path, PATH_MAX, path);
268 if (path[str_size(path) - 1] != '/' &&
269 str_size(subdir_path) + str_size(trailing_slash) + 1 <= PATH_MAX)
270 str_append(subdir_path, PATH_MAX, trailing_slash);
271 if (str_size(subdir_path) +
272 str_size(dir_list[i].name) + 1 <= PATH_MAX)
273 str_append(subdir_path, PATH_MAX, dir_list[i].name);
274
275 scope = ls_scope(subdir_path, &dir_list[i]);
276 switch (scope) {
277 case LS_FILE:
278 break;
279 case LS_DIR:
280 subdirp = opendir(subdir_path);
281 if (!subdirp) {
282 /* May have been deleted between scoping it and opening it */
283 cli_error(CL_EFAIL, "Could not stat %s", dir_list[i].name);
284 ret = CMD_FAILURE;
285 goto out;
286 }
287
288 ret = ls_recursive(subdir_path, subdirp);
289 closedir(subdirp);
290 if (ret == CMD_FAILURE)
291 goto out;
292 break;
293 case LS_BOGUS:
294 ret = CMD_FAILURE;
295 goto out;
296 }
297 }
298
299 ret = CMD_SUCCESS;
300
301out:
302 for (i = 0; i < nbdirs; i++)
303 free(dir_list[i].name);
304 free(dir_list);
305 free(subdir_path);
306
307 return ret;
308}
309
310static unsigned int ls_scope(const char *path, struct dir_elem_t *de)
311{
312 if (vfs_stat_path(path, &de->s) != EOK) {
313 cli_error(CL_ENOENT, "%s", path);
314 return LS_BOGUS;
315 }
316
317 if (de->s.is_file)
318 return LS_FILE;
319 else if (de->s.is_directory)
320 return LS_DIR;
321
322 return LS_BOGUS;
323}
324
325void help_cmd_ls(unsigned int level)
326{
327 if (level == HELP_SHORT) {
328 printf("`%s' lists files and directories.\n", cmdname);
329 } else {
330 help_cmd_ls(HELP_SHORT);
331 printf(
332 "Usage: %s [options] [path]\n"
333 "If not path is given, the current working directory is used.\n"
334 "Options:\n"
335 " -h, --help A short option summary\n"
336 " -u, --unsort Do not sort directory entries\n"
337 " -r, --recursive List subdirectories recursively\n",
338 cmdname);
339 }
340
341 return;
342}
343
344int cmd_ls(char **argv)
345{
346 unsigned int argc;
347 struct dir_elem_t de;
348 DIR *dirp;
349 int c, opt_ind;
350 int ret = 0;
351 unsigned int scope;
352
353 if (!ls_start(&ls)) {
354 cli_error(CL_EFAIL, "%s: Could not initialize", cmdname);
355 return CMD_FAILURE;
356 }
357
358 argc = cli_count_args(argv);
359
360 c = 0;
361 optreset = 1;
362 optind = 0;
363 opt_ind = 0;
364
365 while (c != -1) {
366 c = getopt_long(argc, argv, "hur", long_options, &opt_ind);
367 switch (c) {
368 case 'h':
369 help_cmd_ls(HELP_LONG);
370 return CMD_SUCCESS;
371 case 'u':
372 ls.sort = 0;
373 break;
374 case 'r':
375 ls.recursive = 1;
376 break;
377 }
378 }
379
380 argc -= optind;
381
382 de.name = (char *) malloc(PATH_MAX);
383 if (!de.name) {
384 cli_error(CL_ENOMEM, "%s: Out of memory", cmdname);
385 return CMD_FAILURE;
386 }
387 memset(de.name, 0, PATH_MAX);
388
389 if (argc == 0) {
390 if (vfs_cwd_get(de.name, PATH_MAX) != EOK) {
391 cli_error(CL_EFAIL, "%s: Failed determining working "
392 "directory", cmdname);
393 return CMD_FAILURE;
394 }
395 } else {
396 str_cpy(de.name, PATH_MAX, argv[optind]);
397 }
398
399 scope = ls_scope(de.name, &de);
400 switch (scope) {
401 case LS_FILE:
402 ls_print(&de);
403 break;
404 case LS_DIR:
405 dirp = opendir(de.name);
406 if (!dirp) {
407 /* May have been deleted between scoping it and opening it */
408 cli_error(CL_EFAIL, "Could not stat %s", de.name);
409 free(de.name);
410 return CMD_FAILURE;
411 }
412 if (ls.recursive)
413 ret = ls_recursive(de.name, dirp);
414 else
415 ret = ls_scan_dir(de.name, dirp, NULL);
416
417 closedir(dirp);
418 break;
419 case LS_BOGUS:
420 return CMD_FAILURE;
421 }
422
423 free(de.name);
424
425 if (ret == -1 || ret == CMD_FAILURE)
426 return CMD_FAILURE;
427 else
428 return CMD_SUCCESS;
429}
Note: See TracBrowser for help on using the repository browser.