ls: prevent double open
[grub.git] / grub-core / commands / ls.c
1 /* ls.c - command to list files and devices */
2 /*
3  *  GRUB  --  GRand Unified Bootloader
4  *  Copyright (C) 2003,2005,2007,2008,2009  Free Software Foundation, Inc.
5  *
6  *  GRUB is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  GRUB is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <grub/types.h>
21 #include <grub/misc.h>
22 #include <grub/mm.h>
23 #include <grub/err.h>
24 #include <grub/dl.h>
25 #include <grub/disk.h>
26 #include <grub/device.h>
27 #include <grub/term.h>
28 #include <grub/partition.h>
29 #include <grub/file.h>
30 #include <grub/normal.h>
31 #include <grub/extcmd.h>
32 #include <grub/datetime.h>
33 #include <grub/i18n.h>
34 #include <grub/net.h>
35
36 GRUB_MOD_LICENSE ("GPLv3+");
37
38 static const struct grub_arg_option options[] =
39   {
40     {"long", 'l', 0, N_("Show a long list with more detailed information."), 0, 0},
41     {"human-readable", 'h', 0, N_("Print sizes in a human readable format."), 0, 0},
42     {"all", 'a', 0, N_("List all files."), 0, 0},
43     {0, 0, 0, 0, 0, 0}
44   };
45
46 /* Helper for grub_ls_list_devices.  */
47 static int
48 grub_ls_print_devices (const char *name, void *data)
49 {
50   int *longlist = data;
51
52   if (*longlist)
53     grub_normal_print_device_info (name);
54   else
55     grub_printf ("(%s) ", name);
56
57   return 0;
58 }
59
60 static grub_err_t
61 grub_ls_list_devices (int longlist)
62 {
63   grub_device_iterate (grub_ls_print_devices, &longlist);
64   grub_xputs ("\n");
65
66 #if 0
67   {
68     grub_net_app_level_t proto;
69     int first = 1;
70     FOR_NET_APP_LEVEL (proto)
71     {
72       if (first)
73         grub_puts_ (N_ ("Network protocols:"));
74       first = 0;
75       grub_printf ("%s ", proto->name);
76     }
77     grub_xputs ("\n");
78   }
79 #endif
80
81   grub_refresh ();
82
83   return 0;
84 }
85
86 /* Context for grub_ls_list_files.  */
87 struct grub_ls_list_files_ctx
88 {
89   char *dirname;
90   int all;
91   int human;
92 };
93
94 /* Helper for grub_ls_list_files.  */
95 static int
96 print_files (const char *filename, const struct grub_dirhook_info *info,
97              void *data)
98 {
99   struct grub_ls_list_files_ctx *ctx = data;
100
101   if (ctx->all || filename[0] != '.')
102     grub_printf ("%s%s ", filename, info->dir ? "/" : "");
103
104   return 0;
105 }
106
107 /* Helper for grub_ls_list_files.  */
108 static int
109 print_files_long (const char *filename, const struct grub_dirhook_info *info,
110                   void *data)
111 {
112   struct grub_ls_list_files_ctx *ctx = data;
113
114   if ((! ctx->all) && (filename[0] == '.'))
115     return 0;
116
117   if (! info->dir)
118     {
119       grub_file_t file;
120       char *pathname;
121
122       if (ctx->dirname[grub_strlen (ctx->dirname) - 1] == '/')
123         pathname = grub_xasprintf ("%s%s", ctx->dirname, filename);
124       else
125         pathname = grub_xasprintf ("%s/%s", ctx->dirname, filename);
126
127       if (!pathname)
128         return 1;
129
130       /* XXX: For ext2fs symlinks are detected as files while they
131          should be reported as directories.  */
132       grub_file_filter_disable_compression ();
133       file = grub_file_open (pathname);
134       if (! file)
135         {
136           grub_errno = 0;
137           grub_free (pathname);
138           return 0;
139         }
140
141       if (! ctx->human)
142         grub_printf ("%-12llu", (unsigned long long) file->size);
143       else
144         grub_printf ("%-12s", grub_get_human_size (file->size,
145                                                    GRUB_HUMAN_SIZE_SHORT));
146       grub_file_close (file);
147       grub_free (pathname);
148     }
149   else
150     grub_printf ("%-12s", _("DIR"));
151
152   if (info->mtimeset)
153     {
154       struct grub_datetime datetime;
155       grub_unixtime2datetime (info->mtime, &datetime);
156       if (ctx->human)
157         grub_printf (" %d-%02d-%02d %02d:%02d:%02d %-11s ",
158                      datetime.year, datetime.month, datetime.day,
159                      datetime.hour, datetime.minute,
160                      datetime.second,
161                      grub_get_weekday_name (&datetime));
162       else
163         grub_printf (" %04d%02d%02d%02d%02d%02d ",
164                      datetime.year, datetime.month,
165                      datetime.day, datetime.hour,
166                      datetime.minute, datetime.second);
167     }
168   grub_printf ("%s%s\n", filename, info->dir ? "/" : "");
169
170   return 0;
171 }
172
173 static grub_err_t
174 grub_ls_list_files (char *dirname, int longlist, int all, int human)
175 {
176   char *device_name;
177   grub_fs_t fs;
178   const char *path;
179   grub_device_t dev;
180
181   device_name = grub_file_get_device_name (dirname);
182   dev = grub_device_open (device_name);
183   if (! dev)
184     goto fail;
185
186   fs = grub_fs_probe (dev);
187   path = grub_strchr (dirname, ')');
188   if (! path)
189     path = dirname;
190   else
191     path++;
192
193   if (! path && ! device_name)
194     {
195       grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid argument");
196       goto fail;
197     }
198
199   if (! *path)
200     {
201       if (grub_errno == GRUB_ERR_UNKNOWN_FS)
202         grub_errno = GRUB_ERR_NONE;
203
204 #ifdef GRUB_MACHINE_IEEE1275
205       /*
206        * Close device to prevent a double open in grub_normal_print_device_info().
207        * Otherwise it may lead to hangs on some IEEE 1275 platforms.
208        */
209       grub_device_close (dev);
210       dev = NULL;
211 #endif
212
213       grub_normal_print_device_info (device_name);
214     }
215   else if (fs)
216     {
217       struct grub_ls_list_files_ctx ctx = {
218         .dirname = dirname,
219         .all = all,
220         .human = human
221       };
222
223       if (longlist)
224         (fs->dir) (dev, path, print_files_long, &ctx);
225       else
226         (fs->dir) (dev, path, print_files, &ctx);
227
228       if (grub_errno == GRUB_ERR_BAD_FILE_TYPE
229           && path[grub_strlen (path) - 1] != '/')
230         {
231           /* PATH might be a regular file.  */
232           char *p;
233           grub_file_t file;
234           struct grub_dirhook_info info;
235           grub_errno = 0;
236
237           grub_file_filter_disable_compression ();
238           file = grub_file_open (dirname);
239           if (! file)
240             goto fail;
241
242           grub_file_close (file);
243
244           p = grub_strrchr (dirname, '/') + 1;
245           dirname = grub_strndup (dirname, p - dirname);
246           if (! dirname)
247             goto fail;
248
249           all = 1;
250           grub_memset (&info, 0, sizeof (info));
251           if (longlist)
252             print_files_long (p, &info, &ctx);
253           else
254             print_files (p, &info, &ctx);
255
256           grub_free (dirname);
257         }
258
259       if (grub_errno == GRUB_ERR_NONE)
260         grub_xputs ("\n");
261
262       grub_refresh ();
263     }
264
265  fail:
266   if (dev)
267     grub_device_close (dev);
268
269   grub_free (device_name);
270
271   return 0;
272 }
273
274 static grub_err_t
275 grub_cmd_ls (grub_extcmd_context_t ctxt, int argc, char **args)
276 {
277   struct grub_arg_list *state = ctxt->state;
278   int i;
279
280   if (argc == 0)
281     grub_ls_list_devices (state[0].set);
282   else
283     for (i = 0; i < argc; i++)
284       grub_ls_list_files (args[i], state[0].set, state[2].set,
285                           state[1].set);
286
287   return 0;
288 }
289
290 static grub_extcmd_t cmd;
291
292 GRUB_MOD_INIT(ls)
293 {
294   cmd = grub_register_extcmd ("ls", grub_cmd_ls, 0,
295                               N_("[-l|-h|-a] [FILE ...]"),
296                               N_("List devices and files."), options);
297 }
298
299 GRUB_MOD_FINI(ls)
300 {
301   grub_unregister_extcmd (cmd);
302 }