1987da915Sopenharmony_ci/**
2987da915Sopenharmony_ci * ntfsls - Part of the Linux-NTFS project.
3987da915Sopenharmony_ci *
4987da915Sopenharmony_ci * Copyright (c) 2003 Lode Leroy
5987da915Sopenharmony_ci * Copyright (c) 2003-2005 Anton Altaparmakov
6987da915Sopenharmony_ci * Copyright (c) 2003 Richard Russon
7987da915Sopenharmony_ci * Copyright (c) 2004 Carmelo Kintana
8987da915Sopenharmony_ci * Copyright (c) 2004 Giang Nguyen
9987da915Sopenharmony_ci *
10987da915Sopenharmony_ci * This utility will list a directory's files.
11987da915Sopenharmony_ci *
12987da915Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
13987da915Sopenharmony_ci * it under the terms of the GNU General Public License as published by
14987da915Sopenharmony_ci * the Free Software Foundation; either version 2 of the License, or
15987da915Sopenharmony_ci * (at your option) any later version.
16987da915Sopenharmony_ci *
17987da915Sopenharmony_ci * This program is distributed in the hope that it will be useful,
18987da915Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
19987da915Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20987da915Sopenharmony_ci * GNU General Public License for more details.
21987da915Sopenharmony_ci *
22987da915Sopenharmony_ci * You should have received a copy of the GNU General Public License
23987da915Sopenharmony_ci * along with this program (in the main directory of the Linux-NTFS
24987da915Sopenharmony_ci * distribution in the file COPYING); if not, write to the Free Software
25987da915Sopenharmony_ci * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26987da915Sopenharmony_ci */
27987da915Sopenharmony_ci#include "config.h"
28987da915Sopenharmony_ci
29987da915Sopenharmony_ci#ifdef HAVE_STDIO_H
30987da915Sopenharmony_ci#include <stdio.h>
31987da915Sopenharmony_ci#endif
32987da915Sopenharmony_ci#ifdef HAVE_STDLIB_H
33987da915Sopenharmony_ci#include <stdlib.h>
34987da915Sopenharmony_ci#endif
35987da915Sopenharmony_ci#ifdef HAVE_TIME_H
36987da915Sopenharmony_ci#include <time.h>
37987da915Sopenharmony_ci#endif
38987da915Sopenharmony_ci#ifdef HAVE_GETOPT_H
39987da915Sopenharmony_ci#include <getopt.h>
40987da915Sopenharmony_ci#endif
41987da915Sopenharmony_ci#ifdef HAVE_STRING_H
42987da915Sopenharmony_ci#include <string.h>
43987da915Sopenharmony_ci#endif
44987da915Sopenharmony_ci
45987da915Sopenharmony_ci#include "types.h"
46987da915Sopenharmony_ci#include "mft.h"
47987da915Sopenharmony_ci#include "attrib.h"
48987da915Sopenharmony_ci#include "layout.h"
49987da915Sopenharmony_ci#include "inode.h"
50987da915Sopenharmony_ci#include "utils.h"
51987da915Sopenharmony_ci#include "dir.h"
52987da915Sopenharmony_ci#include "list.h"
53987da915Sopenharmony_ci#include "ntfstime.h"
54987da915Sopenharmony_ci/* #include "version.h" */
55987da915Sopenharmony_ci#include "logging.h"
56987da915Sopenharmony_ci
57987da915Sopenharmony_cistatic const char *EXEC_NAME = "ntfsls";
58987da915Sopenharmony_ci
59987da915Sopenharmony_ci/**
60987da915Sopenharmony_ci * To hold sub-directory information for recursive listing.
61987da915Sopenharmony_ci * @depth:     the level of this dir relative to opts.path
62987da915Sopenharmony_ci */
63987da915Sopenharmony_cistruct dir {
64987da915Sopenharmony_ci	struct ntfs_list_head list;
65987da915Sopenharmony_ci	ntfs_inode *ni;
66987da915Sopenharmony_ci	char name[MAX_PATH];
67987da915Sopenharmony_ci	int depth;
68987da915Sopenharmony_ci};
69987da915Sopenharmony_ci
70987da915Sopenharmony_ci/**
71987da915Sopenharmony_ci * path_component - to store path component strings
72987da915Sopenharmony_ci *
73987da915Sopenharmony_ci * @name: string pointer
74987da915Sopenharmony_ci *
75987da915Sopenharmony_ci * NOTE: @name is not directly allocated memory. It simply points to the
76987da915Sopenharmony_ci * character array name in struct dir.
77987da915Sopenharmony_ci */
78987da915Sopenharmony_cistruct path_component {
79987da915Sopenharmony_ci	struct ntfs_list_head list;
80987da915Sopenharmony_ci	const char *name;
81987da915Sopenharmony_ci};
82987da915Sopenharmony_ci
83987da915Sopenharmony_ci/* The list of sub-dirs is like a "horizontal" tree. The root of
84987da915Sopenharmony_ci * the tree is opts.path, but it is not part of the list because
85987da915Sopenharmony_ci * that's not necessary. The rules of the list are (in order of
86987da915Sopenharmony_ci * precedence):
87987da915Sopenharmony_ci * 1. directories immediately follow their parent.
88987da915Sopenharmony_ci * 2. siblings are next to one another.
89987da915Sopenharmony_ci *
90987da915Sopenharmony_ci * For example, if:
91987da915Sopenharmony_ci *   1. opts.path is /
92987da915Sopenharmony_ci *   2. /    has 2 sub-dirs: dir1 and dir2
93987da915Sopenharmony_ci *   3. dir1 has 2 sub-dirs: dir11 and dir12
94987da915Sopenharmony_ci *   4. dir2 has 0 sub-dirs
95987da915Sopenharmony_ci * then the list will be:
96987da915Sopenharmony_ci * dummy head -> dir1 -> dir11 -> dir12 -> dir2
97987da915Sopenharmony_ci *
98987da915Sopenharmony_ci * dir_list_insert_pos keeps track of where to insert a sub-dir
99987da915Sopenharmony_ci * into the list.
100987da915Sopenharmony_ci */
101987da915Sopenharmony_cistatic struct ntfs_list_head *dir_list_insert_pos = NULL;
102987da915Sopenharmony_ci
103987da915Sopenharmony_ci/* The global depth relative to opts.path.
104987da915Sopenharmony_ci * ie: opts.path has depth 0, a sub-dir of opts.path has depth 1
105987da915Sopenharmony_ci */
106987da915Sopenharmony_cistatic int depth = 0;
107987da915Sopenharmony_ci
108987da915Sopenharmony_cistatic struct options {
109987da915Sopenharmony_ci	char *device;	/* Device/File to work with */
110987da915Sopenharmony_ci	int quiet;	/* Less output */
111987da915Sopenharmony_ci	int verbose;	/* Extra output */
112987da915Sopenharmony_ci	int force;	/* Override common sense */
113987da915Sopenharmony_ci	int all;
114987da915Sopenharmony_ci	int system;
115987da915Sopenharmony_ci	int dos;
116987da915Sopenharmony_ci	int lng;
117987da915Sopenharmony_ci	int inode;
118987da915Sopenharmony_ci	int classify;
119987da915Sopenharmony_ci	int recursive;
120987da915Sopenharmony_ci	const char *path;
121987da915Sopenharmony_ci} opts;
122987da915Sopenharmony_ci
123987da915Sopenharmony_citypedef struct {
124987da915Sopenharmony_ci	ntfs_volume *vol;
125987da915Sopenharmony_ci} ntfsls_dirent;
126987da915Sopenharmony_ci
127987da915Sopenharmony_cistatic int list_dir_entry(ntfsls_dirent * dirent, const ntfschar * name,
128987da915Sopenharmony_ci			  const int name_len, const int name_type,
129987da915Sopenharmony_ci			  const s64 pos, const MFT_REF mref,
130987da915Sopenharmony_ci			  const unsigned dt_type);
131987da915Sopenharmony_ci
132987da915Sopenharmony_ci/**
133987da915Sopenharmony_ci * version - Print version information about the program
134987da915Sopenharmony_ci *
135987da915Sopenharmony_ci * Print a copyright statement and a brief description of the program.
136987da915Sopenharmony_ci *
137987da915Sopenharmony_ci * Return:  none
138987da915Sopenharmony_ci */
139987da915Sopenharmony_cistatic void version(void)
140987da915Sopenharmony_ci{
141987da915Sopenharmony_ci	printf("\n%s v%s (libntfs-3g) - Display information about an NTFS "
142987da915Sopenharmony_ci			"Volume.\n\n", EXEC_NAME, VERSION);
143987da915Sopenharmony_ci	printf("Copyright (c) 2003 Lode Leroy\n");
144987da915Sopenharmony_ci	printf("Copyright (c) 2003-2005 Anton Altaparmakov\n");
145987da915Sopenharmony_ci	printf("Copyright (c) 2003 Richard Russon\n");
146987da915Sopenharmony_ci	printf("Copyright (c) 2004 Carmelo Kintana\n");
147987da915Sopenharmony_ci	printf("Copyright (c) 2004 Giang Nguyen\n");
148987da915Sopenharmony_ci	printf("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home);
149987da915Sopenharmony_ci}
150987da915Sopenharmony_ci
151987da915Sopenharmony_ci/**
152987da915Sopenharmony_ci * usage - Print a list of the parameters to the program
153987da915Sopenharmony_ci *
154987da915Sopenharmony_ci * Print a list of the parameters and options for the program.
155987da915Sopenharmony_ci *
156987da915Sopenharmony_ci * Return:  none
157987da915Sopenharmony_ci */
158987da915Sopenharmony_cistatic void usage(void)
159987da915Sopenharmony_ci{
160987da915Sopenharmony_ci	printf("\nUsage: %s [options] device\n"
161987da915Sopenharmony_ci		"\n"
162987da915Sopenharmony_ci		"    -a, --all            Display all files\n"
163987da915Sopenharmony_ci		"    -F, --classify       Display classification\n"
164987da915Sopenharmony_ci		"    -f, --force          Use less caution\n"
165987da915Sopenharmony_ci		"    -h, --help           Display this help\n"
166987da915Sopenharmony_ci		"    -i, --inode          Display inode numbers\n"
167987da915Sopenharmony_ci		"    -l, --long           Display long info\n"
168987da915Sopenharmony_ci		"    -p, --path PATH      Directory whose contents to list\n"
169987da915Sopenharmony_ci		"    -q, --quiet          Less output\n"
170987da915Sopenharmony_ci		"    -R, --recursive      Recursively list subdirectories\n"
171987da915Sopenharmony_ci		"    -s, --system         Display system files\n"
172987da915Sopenharmony_ci		"    -V, --version        Display version information\n"
173987da915Sopenharmony_ci		"    -v, --verbose        More output\n"
174987da915Sopenharmony_ci		"    -x, --dos            Use short (DOS 8.3) names\n"
175987da915Sopenharmony_ci		"\n",
176987da915Sopenharmony_ci		EXEC_NAME);
177987da915Sopenharmony_ci
178987da915Sopenharmony_ci	printf("NOTE: If neither -a nor -s is specified, the program defaults to -a.\n\n");
179987da915Sopenharmony_ci
180987da915Sopenharmony_ci	printf("%s%s\n", ntfs_bugs, ntfs_home);
181987da915Sopenharmony_ci}
182987da915Sopenharmony_ci
183987da915Sopenharmony_ci/**
184987da915Sopenharmony_ci * parse_options - Read and validate the programs command line
185987da915Sopenharmony_ci *
186987da915Sopenharmony_ci * Read the command line, verify the syntax and parse the options.
187987da915Sopenharmony_ci * This function is very long, but quite simple.
188987da915Sopenharmony_ci *
189987da915Sopenharmony_ci * Return:  1 Success
190987da915Sopenharmony_ci *	    0 Error, one or more problems
191987da915Sopenharmony_ci */
192987da915Sopenharmony_cistatic int parse_options(int argc, char *argv[])
193987da915Sopenharmony_ci{
194987da915Sopenharmony_ci	static const char *sopt = "-aFfh?ilp:qRsVvx";
195987da915Sopenharmony_ci	static const struct option lopt[] = {
196987da915Sopenharmony_ci		{ "all",	 no_argument,		NULL, 'a' },
197987da915Sopenharmony_ci		{ "classify",	 no_argument,		NULL, 'F' },
198987da915Sopenharmony_ci		{ "force",	 no_argument,		NULL, 'f' },
199987da915Sopenharmony_ci		{ "help",	 no_argument,		NULL, 'h' },
200987da915Sopenharmony_ci		{ "inode",	 no_argument,		NULL, 'i' },
201987da915Sopenharmony_ci		{ "long",	 no_argument,		NULL, 'l' },
202987da915Sopenharmony_ci		{ "path",	 required_argument,     NULL, 'p' },
203987da915Sopenharmony_ci		{ "recursive",	 no_argument,		NULL, 'R' },
204987da915Sopenharmony_ci		{ "quiet",	 no_argument,		NULL, 'q' },
205987da915Sopenharmony_ci		{ "system",	 no_argument,		NULL, 's' },
206987da915Sopenharmony_ci		{ "version",	 no_argument,		NULL, 'V' },
207987da915Sopenharmony_ci		{ "verbose",	 no_argument,		NULL, 'v' },
208987da915Sopenharmony_ci		{ "dos",	 no_argument,		NULL, 'x' },
209987da915Sopenharmony_ci		{ NULL, 0, NULL, 0 },
210987da915Sopenharmony_ci	};
211987da915Sopenharmony_ci
212987da915Sopenharmony_ci	int c = -1;
213987da915Sopenharmony_ci	int err  = 0;
214987da915Sopenharmony_ci	int ver  = 0;
215987da915Sopenharmony_ci	int help = 0;
216987da915Sopenharmony_ci	int levels = 0;
217987da915Sopenharmony_ci
218987da915Sopenharmony_ci	opterr = 0; /* We'll handle the errors, thank you. */
219987da915Sopenharmony_ci
220987da915Sopenharmony_ci	memset(&opts, 0, sizeof(opts));
221987da915Sopenharmony_ci	opts.device = NULL;
222987da915Sopenharmony_ci	opts.path = "/";
223987da915Sopenharmony_ci
224987da915Sopenharmony_ci	while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) {
225987da915Sopenharmony_ci		switch (c) {
226987da915Sopenharmony_ci		case 1:
227987da915Sopenharmony_ci			if (!opts.device)
228987da915Sopenharmony_ci				opts.device = optarg;
229987da915Sopenharmony_ci			else
230987da915Sopenharmony_ci				err++;
231987da915Sopenharmony_ci			break;
232987da915Sopenharmony_ci		case 'p':
233987da915Sopenharmony_ci			opts.path = optarg;
234987da915Sopenharmony_ci			break;
235987da915Sopenharmony_ci		case 'f':
236987da915Sopenharmony_ci			opts.force++;
237987da915Sopenharmony_ci			break;
238987da915Sopenharmony_ci		case 'h':
239987da915Sopenharmony_ci		case '?':
240987da915Sopenharmony_ci			if (strncmp (argv[optind-1], "--log-", 6) == 0) {
241987da915Sopenharmony_ci				if (!ntfs_log_parse_option (argv[optind-1]))
242987da915Sopenharmony_ci					err++;
243987da915Sopenharmony_ci				break;
244987da915Sopenharmony_ci			}
245987da915Sopenharmony_ci			help++;
246987da915Sopenharmony_ci			break;
247987da915Sopenharmony_ci		case 'q':
248987da915Sopenharmony_ci			opts.quiet++;
249987da915Sopenharmony_ci			ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET);
250987da915Sopenharmony_ci			break;
251987da915Sopenharmony_ci		case 'v':
252987da915Sopenharmony_ci			opts.verbose++;
253987da915Sopenharmony_ci			ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE);
254987da915Sopenharmony_ci			break;
255987da915Sopenharmony_ci		case 'V':
256987da915Sopenharmony_ci			ver++;
257987da915Sopenharmony_ci			break;
258987da915Sopenharmony_ci		case 'x':
259987da915Sopenharmony_ci			opts.dos = 1;
260987da915Sopenharmony_ci			break;
261987da915Sopenharmony_ci		case 'l':
262987da915Sopenharmony_ci			opts.lng++;
263987da915Sopenharmony_ci			break;
264987da915Sopenharmony_ci		case 'i':
265987da915Sopenharmony_ci			opts.inode++;
266987da915Sopenharmony_ci			break;
267987da915Sopenharmony_ci		case 'F':
268987da915Sopenharmony_ci			opts.classify++;
269987da915Sopenharmony_ci			break;
270987da915Sopenharmony_ci		case 'a':
271987da915Sopenharmony_ci			opts.all++;
272987da915Sopenharmony_ci			break;
273987da915Sopenharmony_ci		case 's':
274987da915Sopenharmony_ci			opts.system++;
275987da915Sopenharmony_ci			break;
276987da915Sopenharmony_ci		case 'R':
277987da915Sopenharmony_ci			opts.recursive++;
278987da915Sopenharmony_ci			break;
279987da915Sopenharmony_ci		default:
280987da915Sopenharmony_ci			ntfs_log_error("Unknown option '%s'.\n", argv[optind - 1]);
281987da915Sopenharmony_ci			err++;
282987da915Sopenharmony_ci			break;
283987da915Sopenharmony_ci		}
284987da915Sopenharmony_ci	}
285987da915Sopenharmony_ci
286987da915Sopenharmony_ci	/* Make sure we're in sync with the log levels */
287987da915Sopenharmony_ci	levels = ntfs_log_get_levels();
288987da915Sopenharmony_ci	if (levels & NTFS_LOG_LEVEL_VERBOSE)
289987da915Sopenharmony_ci		opts.verbose++;
290987da915Sopenharmony_ci	if (!(levels & NTFS_LOG_LEVEL_QUIET))
291987da915Sopenharmony_ci		opts.quiet++;
292987da915Sopenharmony_ci
293987da915Sopenharmony_ci	/* defaults to -a if -s is not specified */
294987da915Sopenharmony_ci	if (!opts.system)
295987da915Sopenharmony_ci		opts.all++;
296987da915Sopenharmony_ci
297987da915Sopenharmony_ci	if (help || ver)
298987da915Sopenharmony_ci		opts.quiet = 0;
299987da915Sopenharmony_ci	else {
300987da915Sopenharmony_ci		if (opts.device == NULL) {
301987da915Sopenharmony_ci			if (argc > 1)
302987da915Sopenharmony_ci				ntfs_log_error("You must specify exactly one "
303987da915Sopenharmony_ci						"device.\n");
304987da915Sopenharmony_ci			err++;
305987da915Sopenharmony_ci		}
306987da915Sopenharmony_ci
307987da915Sopenharmony_ci		if (opts.quiet && opts.verbose) {
308987da915Sopenharmony_ci			ntfs_log_error("You may not use --quiet and --verbose at the "
309987da915Sopenharmony_ci					"same time.\n");
310987da915Sopenharmony_ci			err++;
311987da915Sopenharmony_ci		}
312987da915Sopenharmony_ci	}
313987da915Sopenharmony_ci
314987da915Sopenharmony_ci	if (ver)
315987da915Sopenharmony_ci		version();
316987da915Sopenharmony_ci	if (help || err)
317987da915Sopenharmony_ci		usage();
318987da915Sopenharmony_ci
319987da915Sopenharmony_ci	return (!err && !help && !ver);
320987da915Sopenharmony_ci}
321987da915Sopenharmony_ci
322987da915Sopenharmony_ci/**
323987da915Sopenharmony_ci * free_dir - free one dir
324987da915Sopenharmony_ci * @tofree:   the dir to free
325987da915Sopenharmony_ci *
326987da915Sopenharmony_ci * Close the inode and then free the dir
327987da915Sopenharmony_ci */
328987da915Sopenharmony_cistatic void free_dir(struct dir *tofree)
329987da915Sopenharmony_ci{
330987da915Sopenharmony_ci	if (tofree) {
331987da915Sopenharmony_ci		if (tofree->ni) {
332987da915Sopenharmony_ci			ntfs_inode_close(tofree->ni);
333987da915Sopenharmony_ci			tofree->ni = NULL;
334987da915Sopenharmony_ci		}
335987da915Sopenharmony_ci		free(tofree);
336987da915Sopenharmony_ci	}
337987da915Sopenharmony_ci}
338987da915Sopenharmony_ci
339987da915Sopenharmony_ci/**
340987da915Sopenharmony_ci * free_dirs - walk the list of dir's and free each of them
341987da915Sopenharmony_ci * @dir_list:    the ntfs_list_head of any entry in the list
342987da915Sopenharmony_ci *
343987da915Sopenharmony_ci * Iterate over @dir_list, calling free_dir on each entry
344987da915Sopenharmony_ci */
345987da915Sopenharmony_cistatic void free_dirs(struct ntfs_list_head *dir_list)
346987da915Sopenharmony_ci{
347987da915Sopenharmony_ci	struct dir *tofree = NULL;
348987da915Sopenharmony_ci	struct ntfs_list_head *walker = NULL;
349987da915Sopenharmony_ci
350987da915Sopenharmony_ci	if (dir_list) {
351987da915Sopenharmony_ci		ntfs_list_for_each(walker, dir_list) {
352987da915Sopenharmony_ci			free_dir(tofree);
353987da915Sopenharmony_ci			tofree = ntfs_list_entry(walker, struct dir, list);
354987da915Sopenharmony_ci		}
355987da915Sopenharmony_ci
356987da915Sopenharmony_ci		free_dir(tofree);
357987da915Sopenharmony_ci	}
358987da915Sopenharmony_ci}
359987da915Sopenharmony_ci
360987da915Sopenharmony_ci/**
361987da915Sopenharmony_ci * readdir_recursive - list a directory and sub-directories encountered
362987da915Sopenharmony_ci * @ni:         ntfs inode of the directory to list
363987da915Sopenharmony_ci * @pos:	current position in directory
364987da915Sopenharmony_ci * @dirent:	context for filldir callback supplied by the caller
365987da915Sopenharmony_ci *
366987da915Sopenharmony_ci * For each directory, print its path relative to opts.path. List a directory,
367987da915Sopenharmony_ci * then list each of its sub-directories.
368987da915Sopenharmony_ci *
369987da915Sopenharmony_ci * Returns 0 on success or -1 on error.
370987da915Sopenharmony_ci *
371987da915Sopenharmony_ci * NOTE: Assumes recursive option. Currently no limit on the depths of
372987da915Sopenharmony_ci * recursion.
373987da915Sopenharmony_ci */
374987da915Sopenharmony_cistatic int readdir_recursive(ntfs_inode * ni, s64 * pos, ntfsls_dirent * dirent)
375987da915Sopenharmony_ci{
376987da915Sopenharmony_ci	/* list of dirs to "ls" recursively */
377987da915Sopenharmony_ci	static struct dir dirs = {
378987da915Sopenharmony_ci		.list = NTFS_LIST_HEAD_INIT(dirs.list),
379987da915Sopenharmony_ci		.ni = NULL,
380987da915Sopenharmony_ci		.name = {0},
381987da915Sopenharmony_ci		.depth = 0
382987da915Sopenharmony_ci	};
383987da915Sopenharmony_ci
384987da915Sopenharmony_ci	static struct path_component paths = {
385987da915Sopenharmony_ci		.list = NTFS_LIST_HEAD_INIT(paths.list),
386987da915Sopenharmony_ci		.name = NULL
387987da915Sopenharmony_ci	};
388987da915Sopenharmony_ci
389987da915Sopenharmony_ci	static struct path_component base_comp;
390987da915Sopenharmony_ci
391987da915Sopenharmony_ci	struct dir *subdir = NULL;
392987da915Sopenharmony_ci	struct dir *tofree = NULL;
393987da915Sopenharmony_ci	struct path_component comp;
394987da915Sopenharmony_ci	struct path_component *tempcomp = NULL;
395987da915Sopenharmony_ci	struct ntfs_list_head *dir_walker = NULL;
396987da915Sopenharmony_ci	struct ntfs_list_head *comp_walker = NULL;
397987da915Sopenharmony_ci	s64 pos2 = 0;
398987da915Sopenharmony_ci	int ni_depth = depth;
399987da915Sopenharmony_ci	int result = 0;
400987da915Sopenharmony_ci
401987da915Sopenharmony_ci	if (ntfs_list_empty(&dirs.list)) {
402987da915Sopenharmony_ci		base_comp.name = opts.path;
403987da915Sopenharmony_ci		ntfs_list_add(&base_comp.list, &paths.list);
404987da915Sopenharmony_ci		dir_list_insert_pos = &dirs.list;
405987da915Sopenharmony_ci		printf("%s:\n", opts.path);
406987da915Sopenharmony_ci	}
407987da915Sopenharmony_ci
408987da915Sopenharmony_ci	depth++;
409987da915Sopenharmony_ci
410987da915Sopenharmony_ci	result = ntfs_readdir(ni, pos, dirent, (ntfs_filldir_t) list_dir_entry);
411987da915Sopenharmony_ci
412987da915Sopenharmony_ci	if (result == 0) {
413987da915Sopenharmony_ci		ntfs_list_add_tail(&comp.list, &paths.list);
414987da915Sopenharmony_ci
415987da915Sopenharmony_ci		/* for each of ni's sub-dirs: list in this iteration, then
416987da915Sopenharmony_ci		   free at the top of the next iteration or outside of loop */
417987da915Sopenharmony_ci		ntfs_list_for_each(dir_walker, &dirs.list) {
418987da915Sopenharmony_ci			if (tofree) {
419987da915Sopenharmony_ci				free_dir(tofree);
420987da915Sopenharmony_ci				tofree = NULL;
421987da915Sopenharmony_ci			}
422987da915Sopenharmony_ci			subdir = ntfs_list_entry(dir_walker, struct dir, list);
423987da915Sopenharmony_ci
424987da915Sopenharmony_ci			/* subdir is not a subdir of ni */
425987da915Sopenharmony_ci			if (subdir->depth != ni_depth + 1)
426987da915Sopenharmony_ci				break;
427987da915Sopenharmony_ci
428987da915Sopenharmony_ci			pos2 = 0;
429987da915Sopenharmony_ci			dir_list_insert_pos = &dirs.list;
430987da915Sopenharmony_ci			if (!subdir->ni) {
431987da915Sopenharmony_ci				subdir->ni =
432987da915Sopenharmony_ci				    ntfs_pathname_to_inode(ni->vol, ni,
433987da915Sopenharmony_ci							    subdir->name);
434987da915Sopenharmony_ci
435987da915Sopenharmony_ci				if (!subdir->ni) {
436987da915Sopenharmony_ci					ntfs_log_error
437987da915Sopenharmony_ci					    ("ntfsls::readdir_recursive(): cannot get inode from pathname.\n");
438987da915Sopenharmony_ci					result = -1;
439987da915Sopenharmony_ci					break;
440987da915Sopenharmony_ci				}
441987da915Sopenharmony_ci			}
442987da915Sopenharmony_ci			puts("");
443987da915Sopenharmony_ci
444987da915Sopenharmony_ci			comp.name = subdir->name;
445987da915Sopenharmony_ci
446987da915Sopenharmony_ci			/* print relative path header */
447987da915Sopenharmony_ci			ntfs_list_for_each(comp_walker, &paths.list) {
448987da915Sopenharmony_ci				tempcomp =
449987da915Sopenharmony_ci				    ntfs_list_entry(comp_walker,
450987da915Sopenharmony_ci					       struct path_component, list);
451987da915Sopenharmony_ci				printf("%s", tempcomp->name);
452987da915Sopenharmony_ci				if (tempcomp != &comp
453987da915Sopenharmony_ci				    && *tempcomp->name != PATH_SEP
454987da915Sopenharmony_ci				    && (!opts.classify
455987da915Sopenharmony_ci					|| tempcomp == &base_comp))
456987da915Sopenharmony_ci					putchar(PATH_SEP);
457987da915Sopenharmony_ci			}
458987da915Sopenharmony_ci			puts(":");
459987da915Sopenharmony_ci
460987da915Sopenharmony_ci			result = readdir_recursive(subdir->ni, &pos2, dirent);
461987da915Sopenharmony_ci
462987da915Sopenharmony_ci			if (result)
463987da915Sopenharmony_ci				break;
464987da915Sopenharmony_ci
465987da915Sopenharmony_ci			tofree = subdir;
466987da915Sopenharmony_ci			ntfs_list_del(dir_walker);
467987da915Sopenharmony_ci		}
468987da915Sopenharmony_ci
469987da915Sopenharmony_ci		ntfs_list_del(&comp.list);
470987da915Sopenharmony_ci	}
471987da915Sopenharmony_ci
472987da915Sopenharmony_ci	if (tofree)
473987da915Sopenharmony_ci		free_dir(tofree);
474987da915Sopenharmony_ci
475987da915Sopenharmony_ci	/* if at the outer-most readdir_recursive, then clean up */
476987da915Sopenharmony_ci	if (ni_depth == 0) {
477987da915Sopenharmony_ci		free_dirs(&dirs.list);
478987da915Sopenharmony_ci	}
479987da915Sopenharmony_ci
480987da915Sopenharmony_ci	depth--;
481987da915Sopenharmony_ci
482987da915Sopenharmony_ci	return result;
483987da915Sopenharmony_ci}
484987da915Sopenharmony_ci
485987da915Sopenharmony_ci/**
486987da915Sopenharmony_ci * list_dir_entry
487987da915Sopenharmony_ci *
488987da915Sopenharmony_ci * FIXME: Should we print errors as we go along? (AIA)
489987da915Sopenharmony_ci */
490987da915Sopenharmony_cistatic int list_dir_entry(ntfsls_dirent * dirent, const ntfschar * name,
491987da915Sopenharmony_ci			  const int name_len, const int name_type,
492987da915Sopenharmony_ci			  const s64 pos __attribute__((unused)),
493987da915Sopenharmony_ci			  const MFT_REF mref, const unsigned dt_type)
494987da915Sopenharmony_ci{
495987da915Sopenharmony_ci	char *filename = NULL;
496987da915Sopenharmony_ci	int result = 0;
497987da915Sopenharmony_ci
498987da915Sopenharmony_ci	struct dir *dir = NULL;
499987da915Sopenharmony_ci
500987da915Sopenharmony_ci	filename = calloc(1, MAX_PATH);
501987da915Sopenharmony_ci	if (!filename)
502987da915Sopenharmony_ci		return -1;
503987da915Sopenharmony_ci
504987da915Sopenharmony_ci	if (ntfs_ucstombs(name, name_len, &filename, MAX_PATH) < 0) {
505987da915Sopenharmony_ci		ntfs_log_error("Cannot represent filename in current locale.\n");
506987da915Sopenharmony_ci		goto free;
507987da915Sopenharmony_ci	}
508987da915Sopenharmony_ci
509987da915Sopenharmony_ci	result = 0;					// These are successful
510987da915Sopenharmony_ci	if ((MREF(mref) < FILE_first_user) && (!opts.system))
511987da915Sopenharmony_ci		goto free;
512987da915Sopenharmony_ci	if (name_type == FILE_NAME_POSIX && !opts.all)
513987da915Sopenharmony_ci		goto free;
514987da915Sopenharmony_ci	if (((name_type & FILE_NAME_WIN32_AND_DOS) == FILE_NAME_WIN32) &&
515987da915Sopenharmony_ci			opts.dos)
516987da915Sopenharmony_ci		goto free;
517987da915Sopenharmony_ci	if (((name_type & FILE_NAME_WIN32_AND_DOS) == FILE_NAME_DOS) &&
518987da915Sopenharmony_ci			!opts.dos)
519987da915Sopenharmony_ci		goto free;
520987da915Sopenharmony_ci	if (dt_type == NTFS_DT_DIR && opts.classify)
521987da915Sopenharmony_ci		sprintf(filename + strlen(filename), "/");
522987da915Sopenharmony_ci
523987da915Sopenharmony_ci	if (dt_type == NTFS_DT_DIR && opts.recursive
524987da915Sopenharmony_ci	    && strcmp(filename, ".") && strcmp(filename, "./")
525987da915Sopenharmony_ci	    && strcmp(filename, "..") && strcmp(filename, "../"))
526987da915Sopenharmony_ci	{
527987da915Sopenharmony_ci		dir = (struct dir *)calloc(1, sizeof(struct dir));
528987da915Sopenharmony_ci
529987da915Sopenharmony_ci		if (!dir) {
530987da915Sopenharmony_ci			ntfs_log_error("Failed to allocate for subdir.\n");
531987da915Sopenharmony_ci			result = -1;
532987da915Sopenharmony_ci			goto free;
533987da915Sopenharmony_ci		}
534987da915Sopenharmony_ci
535987da915Sopenharmony_ci		strcpy(dir->name, filename);
536987da915Sopenharmony_ci		dir->ni = NULL;
537987da915Sopenharmony_ci		dir->depth = depth;
538987da915Sopenharmony_ci	}
539987da915Sopenharmony_ci
540987da915Sopenharmony_ci	if (!opts.lng) {
541987da915Sopenharmony_ci		if (!opts.inode)
542987da915Sopenharmony_ci			printf("%s\n", filename);
543987da915Sopenharmony_ci		else
544987da915Sopenharmony_ci			printf("%7llu %s\n", (unsigned long long)MREF(mref),
545987da915Sopenharmony_ci					filename);
546987da915Sopenharmony_ci		result = 0;
547987da915Sopenharmony_ci	} else {
548987da915Sopenharmony_ci		s64 filesize = 0;
549987da915Sopenharmony_ci		ntfs_inode *ni;
550987da915Sopenharmony_ci		ntfs_attr_search_ctx *ctx = NULL;
551987da915Sopenharmony_ci		FILE_NAME_ATTR *file_name_attr;
552987da915Sopenharmony_ci		ATTR_RECORD *attr;
553987da915Sopenharmony_ci		struct timespec change_time;
554987da915Sopenharmony_ci		char t_buf[26];
555987da915Sopenharmony_ci
556987da915Sopenharmony_ci		result = -1;				// Everything else is bad
557987da915Sopenharmony_ci
558987da915Sopenharmony_ci		ni = ntfs_inode_open(dirent->vol, mref);
559987da915Sopenharmony_ci		if (!ni)
560987da915Sopenharmony_ci			goto release;
561987da915Sopenharmony_ci
562987da915Sopenharmony_ci		ctx = ntfs_attr_get_search_ctx(ni, NULL);
563987da915Sopenharmony_ci		if (!ctx)
564987da915Sopenharmony_ci			goto release;
565987da915Sopenharmony_ci
566987da915Sopenharmony_ci		if (ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, 0, 0, NULL,
567987da915Sopenharmony_ci				0, ctx))
568987da915Sopenharmony_ci			goto release;
569987da915Sopenharmony_ci		attr = ctx->attr;
570987da915Sopenharmony_ci
571987da915Sopenharmony_ci		file_name_attr = (FILE_NAME_ATTR *)((char *)attr +
572987da915Sopenharmony_ci				le16_to_cpu(attr->value_offset));
573987da915Sopenharmony_ci		if (!file_name_attr)
574987da915Sopenharmony_ci			goto release;
575987da915Sopenharmony_ci
576987da915Sopenharmony_ci		change_time = ntfs2timespec(file_name_attr->last_data_change_time);
577987da915Sopenharmony_ci		strcpy(t_buf, ctime(&change_time.tv_sec));
578987da915Sopenharmony_ci		memmove(t_buf+16, t_buf+19, 5);
579987da915Sopenharmony_ci		t_buf[21] = '\0';
580987da915Sopenharmony_ci
581987da915Sopenharmony_ci		if (dt_type != NTFS_DT_DIR) {
582987da915Sopenharmony_ci			if (!ntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, 0, 0,
583987da915Sopenharmony_ci					NULL, 0, ctx))
584987da915Sopenharmony_ci				filesize = ntfs_get_attribute_value_length(
585987da915Sopenharmony_ci						ctx->attr);
586987da915Sopenharmony_ci		}
587987da915Sopenharmony_ci
588987da915Sopenharmony_ci		if (opts.inode)
589987da915Sopenharmony_ci			printf("%7llu    %8lld %s %s\n",
590987da915Sopenharmony_ci					(unsigned long long)MREF(mref),
591987da915Sopenharmony_ci					(long long)filesize, t_buf + 4,
592987da915Sopenharmony_ci					filename);
593987da915Sopenharmony_ci		else
594987da915Sopenharmony_ci			printf("%8lld %s %s\n", (long long)filesize, t_buf + 4,
595987da915Sopenharmony_ci					filename);
596987da915Sopenharmony_ci
597987da915Sopenharmony_ci		if (dir) {
598987da915Sopenharmony_ci			dir->ni = ni;
599987da915Sopenharmony_ci			ni = NULL;	/* so release does not close inode */
600987da915Sopenharmony_ci		}
601987da915Sopenharmony_ci
602987da915Sopenharmony_ci		result = 0;
603987da915Sopenharmony_cirelease:
604987da915Sopenharmony_ci		/* Release attribute search context and close the inode. */
605987da915Sopenharmony_ci		if (ctx)
606987da915Sopenharmony_ci			ntfs_attr_put_search_ctx(ctx);
607987da915Sopenharmony_ci		if (ni)
608987da915Sopenharmony_ci			ntfs_inode_close(ni);
609987da915Sopenharmony_ci	}
610987da915Sopenharmony_ci
611987da915Sopenharmony_ci	if (dir) {
612987da915Sopenharmony_ci		if (result == 0) {
613987da915Sopenharmony_ci			ntfs_list_add(&dir->list, dir_list_insert_pos);
614987da915Sopenharmony_ci			dir_list_insert_pos = &dir->list;
615987da915Sopenharmony_ci		} else {
616987da915Sopenharmony_ci			free(dir);
617987da915Sopenharmony_ci			dir = NULL;
618987da915Sopenharmony_ci		}
619987da915Sopenharmony_ci	}
620987da915Sopenharmony_ci
621987da915Sopenharmony_cifree:
622987da915Sopenharmony_ci	free(filename);
623987da915Sopenharmony_ci	return result;
624987da915Sopenharmony_ci}
625987da915Sopenharmony_ci
626987da915Sopenharmony_ci/**
627987da915Sopenharmony_ci * main - Begin here
628987da915Sopenharmony_ci *
629987da915Sopenharmony_ci * Start from here.
630987da915Sopenharmony_ci *
631987da915Sopenharmony_ci * Return:  0  Success, the program worked
632987da915Sopenharmony_ci *	    1  Error, parsing mount options failed
633987da915Sopenharmony_ci *	    2  Error, mount attempt failed
634987da915Sopenharmony_ci *	    3  Error, failed to open root directory
635987da915Sopenharmony_ci *	    4  Error, failed to open directory in search path
636987da915Sopenharmony_ci */
637987da915Sopenharmony_ciint main(int argc, char **argv)
638987da915Sopenharmony_ci{
639987da915Sopenharmony_ci	s64 pos;
640987da915Sopenharmony_ci	ntfs_volume *vol;
641987da915Sopenharmony_ci	ntfs_inode *ni;
642987da915Sopenharmony_ci	ntfsls_dirent dirent;
643987da915Sopenharmony_ci
644987da915Sopenharmony_ci	ntfs_log_set_handler(ntfs_log_handler_outerr);
645987da915Sopenharmony_ci
646987da915Sopenharmony_ci	if (!parse_options(argc, argv)) {
647987da915Sopenharmony_ci		// FIXME: Print error... (AIA)
648987da915Sopenharmony_ci		return 1;
649987da915Sopenharmony_ci	}
650987da915Sopenharmony_ci
651987da915Sopenharmony_ci	utils_set_locale();
652987da915Sopenharmony_ci
653987da915Sopenharmony_ci	vol = utils_mount_volume(opts.device, NTFS_MNT_RDONLY |
654987da915Sopenharmony_ci			(opts.force ? NTFS_MNT_RECOVER : 0));
655987da915Sopenharmony_ci	if (!vol) {
656987da915Sopenharmony_ci		// FIXME: Print error... (AIA)
657987da915Sopenharmony_ci		return 2;
658987da915Sopenharmony_ci	}
659987da915Sopenharmony_ci
660987da915Sopenharmony_ci	ni = ntfs_pathname_to_inode(vol, NULL, opts.path);
661987da915Sopenharmony_ci	if (!ni) {
662987da915Sopenharmony_ci		// FIXME: Print error... (AIA)
663987da915Sopenharmony_ci		ntfs_umount(vol, FALSE);
664987da915Sopenharmony_ci		return 3;
665987da915Sopenharmony_ci	}
666987da915Sopenharmony_ci
667987da915Sopenharmony_ci	/*
668987da915Sopenharmony_ci	 * We now are at the final path component.  If it is a file just
669987da915Sopenharmony_ci	 * list it.  If it is a directory, list its contents.
670987da915Sopenharmony_ci	 */
671987da915Sopenharmony_ci	pos = 0;
672987da915Sopenharmony_ci	memset(&dirent, 0, sizeof(dirent));
673987da915Sopenharmony_ci	dirent.vol = vol;
674987da915Sopenharmony_ci	if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
675987da915Sopenharmony_ci		if (opts.recursive)
676987da915Sopenharmony_ci			readdir_recursive(ni, &pos, &dirent);
677987da915Sopenharmony_ci		else
678987da915Sopenharmony_ci			ntfs_readdir(ni, &pos, &dirent,
679987da915Sopenharmony_ci				     (ntfs_filldir_t) list_dir_entry);
680987da915Sopenharmony_ci		// FIXME: error checking... (AIA)
681987da915Sopenharmony_ci	} else {
682987da915Sopenharmony_ci		ATTR_RECORD *rec;
683987da915Sopenharmony_ci		FILE_NAME_ATTR *attr;
684987da915Sopenharmony_ci		ntfs_attr_search_ctx *ctx;
685987da915Sopenharmony_ci		int space = 4;
686987da915Sopenharmony_ci		ntfschar *name = NULL;
687987da915Sopenharmony_ci		int name_len = 0;;
688987da915Sopenharmony_ci
689987da915Sopenharmony_ci		ctx = ntfs_attr_get_search_ctx(ni, NULL);
690987da915Sopenharmony_ci		if (!ctx)
691987da915Sopenharmony_ci			return -1;
692987da915Sopenharmony_ci
693987da915Sopenharmony_ci		while ((rec = find_attribute(AT_FILE_NAME, ctx))) {
694987da915Sopenharmony_ci			/* We know this will always be resident. */
695987da915Sopenharmony_ci			attr = (FILE_NAME_ATTR *) ((char *) rec + le16_to_cpu(rec->value_offset));
696987da915Sopenharmony_ci
697987da915Sopenharmony_ci			if (attr->file_name_type < space) {
698987da915Sopenharmony_ci				name     = attr->file_name;
699987da915Sopenharmony_ci				name_len = attr->file_name_length;
700987da915Sopenharmony_ci				space    = attr->file_name_type;
701987da915Sopenharmony_ci			}
702987da915Sopenharmony_ci		}
703987da915Sopenharmony_ci
704987da915Sopenharmony_ci		list_dir_entry(&dirent, name, name_len, space, pos, ni->mft_no,
705987da915Sopenharmony_ci			       NTFS_DT_REG);
706987da915Sopenharmony_ci		// FIXME: error checking... (AIA)
707987da915Sopenharmony_ci
708987da915Sopenharmony_ci		ntfs_attr_put_search_ctx(ctx);
709987da915Sopenharmony_ci	}
710987da915Sopenharmony_ci
711987da915Sopenharmony_ci	/* Finished with the inode; release it. */
712987da915Sopenharmony_ci	ntfs_inode_close(ni);
713987da915Sopenharmony_ci
714987da915Sopenharmony_ci	ntfs_umount(vol, FALSE);
715987da915Sopenharmony_ci	return 0;
716987da915Sopenharmony_ci}
717987da915Sopenharmony_ci
718