1 /* stat - load up an associative array with stat information about a file */
2 
3 /* See Makefile for compilation details. */
4 
5 /*
6    Copyright (C) 2016 Free Software Foundation, Inc.
7 
8    This file is part of GNU Bash.
9    Bash 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 3 of the License, or
12    (at your option) any later version.
13 
14    Bash 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 Bash.  If not, see <http://www.gnu.org/licenses/>.
21 */
22 
23 #include <config.h>
24 
25 #if defined (HAVE_UNISTD_H)
26 #  include <unistd.h>
27 #endif
28 
29 #include <stdio.h>
30 
31 #include <sys/types.h>
32 #include "posixstat.h"
33 #include <stdio.h>
34 #include <pwd.h>
35 #include <grp.h>
36 #include <errno.h>
37 #include "posixtime.h"
38 
39 #include "bashansi.h"
40 #include "shell.h"
41 #include "builtins.h"
42 #include "common.h"
43 #include "bashgetopt.h"
44 
45 #ifndef errno
46 extern int	errno;
47 #endif
48 
49 #define ST_NAME		0
50 #define ST_DEV		1
51 #define ST_INO		2
52 #define ST_MODE		3
53 #define ST_NLINK	4
54 #define ST_UID		5
55 #define ST_GID		6
56 #define ST_RDEV		7
57 #define ST_SIZE		8
58 #define ST_ATIME	9
59 #define ST_MTIME	10
60 #define ST_CTIME	11
61 #define ST_BLKSIZE	12
62 #define ST_BLOCKS	13
63 #define ST_CHASELINK	14
64 #define ST_PERMS	15
65 
66 #define ST_END		16
67 
68 static char *arraysubs[] =
69   {
70     "name", "device", "inode", "type", "nlink", "uid", "gid", "rdev",
71     "size", "atime", "mtime", "ctime", "blksize", "blocks", "link", "perms",
72     0
73   };
74 
75 static int
getstat(fname,flags,sp)76 getstat (fname, flags, sp)
77      const char *fname;
78      int flags;
79      struct stat *sp;
80 {
81   intmax_t lfd;
82   int fd, r;
83 
84   if (strncmp (fname, "/dev/fd/", 8) == 0)
85     {
86       if ((legal_number(fname + 8, &lfd) == 0) || (int)lfd != lfd)
87 	{
88 	  errno = EINVAL;
89 	  return -1;
90 	}
91       fd = lfd;
92       r = fstat(fd, sp);
93     }
94 #ifdef HAVE_LSTAT
95   else if (flags & 1)
96     r = lstat(fname, sp);
97 #endif
98   else
99     r = stat(fname, sp);
100 
101   return r;
102 }
103 
104 static char *
statlink(fname,sp)105 statlink (fname, sp)
106      char *fname;
107      struct stat *sp;
108 {
109 #if defined (HAVE_READLINK)
110   char linkbuf[PATH_MAX];
111   int n;
112 
113   if (fname && S_ISLNK (sp->st_mode) && (n = readlink (fname, linkbuf, PATH_MAX)) > 0)
114     {
115       linkbuf[n] = '\0';
116       return (savestring (linkbuf));
117     }
118   else
119 #endif
120     return (savestring (fname));
121 }
122 
123 static char *
octalperms(m)124 octalperms (m)
125      int m;
126 {
127   int operms;
128   char *ret;
129 
130   operms = 0;
131 
132   if (m & S_IRUSR)
133     operms |= 0400;
134   if (m & S_IWUSR)
135     operms |= 0200;
136   if (m & S_IXUSR)
137     operms |= 0100;
138 
139   if (m & S_IRGRP)
140     operms |= 0040;
141   if (m & S_IWGRP)
142     operms |= 0020;
143   if (m & S_IXGRP)
144     operms |= 0010;
145 
146   if (m & S_IROTH)
147     operms |= 0004;
148   if (m & S_IWOTH)
149     operms |= 0002;
150   if (m & S_IXOTH)
151     operms |= 0001;
152 
153   if (m & S_ISUID)
154     operms |= 04000;
155   if (m & S_ISGID)
156     operms |= 02000;
157   if (m & S_ISVTX)
158     operms |= 01000;
159 
160   ret = (char *)xmalloc (16);
161   snprintf (ret, 16, "%04o", operms);
162   return ret;
163 }
164 
165 static char *
statperms(m)166 statperms (m)
167      int m;
168 {
169   char ubits[4], gbits[4], obits[4];	/* u=rwx,g=rwx,o=rwx */
170   int i;
171   char *ret;
172 
173   i = 0;
174   if (m & S_IRUSR)
175     ubits[i++] = 'r';
176   if (m & S_IWUSR)
177     ubits[i++] = 'w';
178   if (m & S_IXUSR)
179     ubits[i++] = 'x';
180   ubits[i] = '\0';
181 
182   i = 0;
183   if (m & S_IRGRP)
184     gbits[i++] = 'r';
185   if (m & S_IWGRP)
186     gbits[i++] = 'w';
187   if (m & S_IXGRP)
188     gbits[i++] = 'x';
189   gbits[i] = '\0';
190 
191   i = 0;
192   if (m & S_IROTH)
193     obits[i++] = 'r';
194   if (m & S_IWOTH)
195     obits[i++] = 'w';
196   if (m & S_IXOTH)
197     obits[i++] = 'x';
198   obits[i] = '\0';
199 
200   if (m & S_ISUID)
201     ubits[2] = (m & S_IXUSR) ? 's' : 'S';
202   if (m & S_ISGID)
203     gbits[2] = (m & S_IXGRP) ? 's' : 'S';
204   if (m & S_ISVTX)
205     obits[2] = (m & S_IXOTH) ? 't' : 'T';
206 
207   ret = (char *)xmalloc (32);
208   snprintf (ret, 32, "u=%s,g=%s,o=%s", ubits, gbits, obits);
209   return ret;
210 }
211 
212 static char *
statmode(mode)213 statmode(mode)
214      int mode;
215 {
216   char *modestr, *m;
217 
218   modestr = m = (char *)xmalloc (8);
219   if (S_ISBLK (mode))
220     *m++ = 'b';
221   if (S_ISCHR (mode))
222     *m++ = 'c';
223   if (S_ISDIR (mode))
224     *m++ = 'd';
225   if (S_ISREG(mode))
226     *m++ = '-';
227   if (S_ISFIFO(mode))
228     *m++ = 'p';
229   if (S_ISLNK(mode))
230     *m++ = 'l';
231   if (S_ISSOCK(mode))
232     *m++ = 's';
233 
234 #ifdef S_ISDOOR
235   if (S_ISDOOR (mode))
236     *m++ = 'D';
237 #endif
238 #ifdef S_ISWHT
239   if (S_ISWHT(mode))
240     *m++ = 'W';
241 #endif
242 #ifdef S_ISNWK
243   if (S_ISNWK(mode))
244     *m++ = 'n';
245 #endif
246 #ifdef S_ISMPC
247   if (S_ISMPC (mode))
248     *m++ = 'm';
249 #endif
250 
251   *m = '\0';
252   return (modestr);
253 }
254 
255 static char *
stattime(t)256 stattime (t)
257      time_t t;
258 {
259   char *tbuf, *ret;
260   size_t tlen;
261 
262   tbuf = ctime (&t);
263   tlen = strlen (tbuf);
264   ret = savestring (tbuf);
265   ret[tlen-1] = '\0';
266   return ret;
267 }
268 
269 static char *
statval(which,fname,flags,sp)270 statval (which, fname, flags, sp)
271      int which;
272      char *fname;
273      int flags;
274      struct stat *sp;
275 {
276   int temp;
277 
278   switch (which)
279     {
280     case ST_NAME:
281       return savestring (fname);
282     case ST_DEV:
283       return itos (sp->st_dev);
284     case ST_INO:
285       return itos (sp->st_ino);
286     case ST_MODE:
287       return (statmode (sp->st_mode));
288     case ST_NLINK:
289       return itos (sp->st_nlink);
290     case ST_UID:
291       return itos (sp->st_uid);
292     case ST_GID:
293       return itos (sp->st_gid);
294     case ST_RDEV:
295       return itos (sp->st_rdev);
296     case ST_SIZE:
297       return itos (sp->st_size);
298     case ST_ATIME:
299       return ((flags & 2) ? stattime (sp->st_atime) : itos (sp->st_atime));
300     case ST_MTIME:
301       return ((flags & 2) ? stattime (sp->st_mtime) : itos (sp->st_mtime));
302     case ST_CTIME:
303       return ((flags & 2) ? stattime (sp->st_ctime) : itos (sp->st_ctime));
304     case ST_BLKSIZE:
305       return itos (sp->st_blksize);
306     case ST_BLOCKS:
307       return itos (sp->st_blocks);
308     case ST_CHASELINK:
309       return (statlink (fname, sp));
310     case ST_PERMS:
311       temp = sp->st_mode & (S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID);
312       return (flags & 2) ? statperms (temp) : octalperms (temp);
313     default:
314       return savestring ("42");
315     }
316 }
317 
318 static int
loadstat(vname,var,fname,flags,sp)319 loadstat (vname, var, fname, flags, sp)
320      char *vname;
321      SHELL_VAR *var;
322      char *fname;
323      int flags;
324      struct stat *sp;
325 {
326   int i;
327   char *key, *value;
328   SHELL_VAR *v;
329 
330   for (i = 0; arraysubs[i]; i++)
331     {
332       key = savestring (arraysubs[i]);
333       value = statval (i, fname, flags, sp);
334       v = bind_assoc_variable (var, vname, key, value, ASS_FORCE);
335     }
336   return 0;
337 }
338 
339 int
stat_builtin(list)340 stat_builtin (list)
341      WORD_LIST *list;
342 {
343   int opt, flags;
344   char *aname, *fname;
345   struct stat st;
346   SHELL_VAR *v;
347 
348   aname = "STAT";
349   flags = 0;
350 
351   reset_internal_getopt ();
352   while ((opt = internal_getopt (list, "A:Ll")) != -1)
353     {
354       switch (opt)
355 	{
356 	case 'A':
357 	  aname = list_optarg;
358 	  break;
359 	case 'L':
360 	  flags |= 1;		/* operate on links rather than resolving them */
361 	  break;
362 	case 'l':
363 	  flags |= 2;
364 	  break;
365 	CASE_HELPOPT;
366 	default:
367 	  builtin_usage ();
368 	  return (EX_USAGE);
369 	}
370     }
371 
372   list = loptend;
373   if (list == 0)
374     {
375       builtin_usage ();
376       return (EX_USAGE);
377     }
378 
379   fname = list->word->word;
380 
381   if (getstat (fname, flags, &st) < 0)
382     {
383       builtin_error ("%s: cannot stat: %s", fname, strerror (errno));
384       return (EXECUTION_FAILURE);
385     }
386 
387   unbind_variable (aname);
388   v = make_new_assoc_variable (aname);
389   if (v == 0)
390     {
391       builtin_error ("%s: cannot create variable", aname);
392       return (EXECUTION_FAILURE);
393     }
394   if (loadstat (aname, v, fname, flags, &st) < 0)
395     {
396       builtin_error ("%s: cannot assign file status information", aname);
397       unbind_variable (aname);
398       return (EXECUTION_FAILURE);
399     }
400 
401   return (EXECUTION_SUCCESS);
402 }
403 
404 /* An array of strings forming the `long' documentation for a builtin xxx,
405    which is printed by `help xxx'.  It must end with a NULL.  By convention,
406    the first line is a short description. */
407 char *stat_doc[] = {
408 	"Load an associative array with file status information.",
409 	"",
410 	"Take a filename and load the status information returned by a",
411 	"stat(2) call on that file into the associative array specified",
412 	"by the -A option.  The default array name is STAT.  If the -L",
413 	"option is supplied, stat does not resolve symbolic links and",
414 	"reports information about the link itself.  The -l option results",
415 	"in longer-form listings for some of the fields. The exit status is 0",
416 	"unless the stat fails or assigning the array is unsuccessful.",
417 	(char *)NULL
418 };
419 
420 /* The standard structure describing a builtin command.  bash keeps an array
421    of these structures.  The flags must include BUILTIN_ENABLED so the
422    builtin can be used. */
423 struct builtin stat_struct = {
424 	"stat",			/* builtin name */
425 	stat_builtin,		/* function implementing the builtin */
426 	BUILTIN_ENABLED,	/* initial flags for builtin */
427 	stat_doc,		/* array of long documentation strings. */
428 	"stat [-lL] [-A aname] file",	/* usage synopsis; becomes short_doc */
429 	0			/* reserved for internal use */
430 };
431