rpm  5.2.1
fs.c
Go to the documentation of this file.
1 
5 #include "system.h"
6 #include <rpmio.h>
7 #include <rpmiotypes.h>
8 #include <rpmlog.h>
9 #include <rpmmacro.h> /* XXX for rpmGetPath */
10 
11 #include "fs.h"
12 
13 #include "debug.h"
14 
15 /*@-usereleased -onlytrans@*/
16 
17 struct fsinfo {
18 /*@only@*/ /*@relnull@*/
19  const char * mntPoint;
20  dev_t dev;
21  int rdonly;
22 };
23 
24 /*@unchecked@*/
25 /*@only@*/ /*@null@*/
26 static struct fsinfo * filesystems = NULL;
27 /*@unchecked@*/
28 /*@only@*/ /*@null@*/
29 static const char ** fsnames = NULL;
30 /*@unchecked@*/
31 static int numFilesystems = 0;
32 
34  /*@globals filesystems, fsnames, numFilesystems @*/
35  /*@modifies filesystems, fsnames, numFilesystems @*/
36 {
37  int i;
38 
39  if (filesystems)
40  for (i = 0; i < numFilesystems; i++)
41  filesystems[i].mntPoint = _free(filesystems[i].mntPoint);
42 
43  filesystems = _free(filesystems);
45  numFilesystems = 0;
46 }
47 
48 #if defined(HAVE_MNTCTL)
49 
50 /* modeled after sample code from Till Bubeck */
51 
52 #include <sys/mntctl.h>
53 #include <sys/vmount.h>
54 
55 /*
56  * There is NO mntctl prototype in any header file of AIX 3.2.5!
57  * So we have to declare it by ourself...
58  */
59 int mntctl(int command, int size, char *buffer);
60 
66 static int getFilesystemList(void)
67  /*@*/
68 {
69  int size;
70  void * buf;
71  struct vmount * vm;
72  struct stat sb;
73  int rdonly = 0;
74  int num;
75  int fsnameLength;
76  int i;
77 
78  num = mntctl(MCTL_QUERY, sizeof(size), (char *) &size);
79  if (num < 0) {
80  rpmlog(RPMLOG_ERR, _("mntctl() failed to return size: %s\n"),
81  strerror(errno));
82  return 1;
83  }
84 
85  /*
86  * Double the needed size, so that even when the user mounts a
87  * filesystem between the previous and the next call to mntctl
88  * the buffer still is large enough.
89  */
90  size *= 2;
91 
92  buf = alloca(size);
93  num = mntctl(MCTL_QUERY, size, buf);
94  if ( num <= 0 ) {
95  rpmlog(RPMLOG_ERR, _("mntctl() failed to return mount points: %s\n"),
96  strerror(errno));
97  return 1;
98  }
99 
100  numFilesystems = num;
101 
102  filesystems = xcalloc((numFilesystems + 1), sizeof(*filesystems));
103  fsnames = xcalloc((numFilesystems + 1), sizeof(char *));
104 
105  for (vm = buf, i = 0; i < num; i++) {
106  char *fsn;
107  fsnameLength = vm->vmt_data[VMT_STUB].vmt_size;
108  fsn = xmalloc(fsnameLength + 1);
109  strncpy(fsn, (char *)vm + vm->vmt_data[VMT_STUB].vmt_off,
110  fsnameLength);
111 
112  filesystems[i].mntPoint = fsnames[i] = fsn;
113 
114 #if defined(RPM_VENDOR_OPENPKG) /* always-skip-proc-filesystem */
115  if (!(strcmp(fsn, "/proc") == 0)) {
116 #endif
117  if (Stat(fsn, &sb) < 0) {
118  switch(errno) {
119  default:
120  rpmlog(RPMLOG_ERR, _("failed to stat %s: %s\n"), fsn,
121  strerror(errno));
123  return 1;
124  case ENOENT: /* XXX avoid /proc if leaked into *BSD jails. */
125  sb.st_dev = 0; /* XXXX make sure st_dev is initialized. */
126  /*@switchbreak@*/ break;
127  }
128  }
129 
130  filesystems[i].dev = sb.st_dev;
131  filesystems[i].rdonly = rdonly;
132 #if defined(RPM_VENDOR_OPENPKG) /* always-skip-proc-filesystem */
133  }
134 #endif
135 
136  /* goto the next vmount structure: */
137  vm = (struct vmount *)((char *)vm + vm->vmt_length);
138  }
139 
140  filesystems[i].mntPoint = NULL;
141  fsnames[i] = NULL;
142 
143  return 0;
144 }
145 
146 #else /* HAVE_MNTCTL */
147 
153 static int getFilesystemList(void)
154  /*@globals h_errno, filesystems, fsnames, numFilesystems,
155  fileSystem, internalState @*/
156  /*@modifies filesystems, fsnames, numFilesystems,
157  fileSystem, internalState @*/
158 {
159  int numAlloced = 10;
160  struct stat sb;
161  int i;
162  const char * mntdir;
163  int rdonly = 0;
164 
165 # if GETMNTENT_ONE || GETMNTENT_TWO
166  our_mntent item;
167  FILE * mtab;
168 
169  mtab = fopen(MOUNTED, "r");
170  if (!mtab) {
171  rpmlog(RPMLOG_ERR, _("failed to open %s: %s\n"), MOUNTED,
172  strerror(errno));
173  return 1;
174  }
175 # elif defined(HAVE_GETMNTINFO_R)
176  /* This is OSF */
177  struct statfs * mounts = NULL;
178  int mntCount = 0, bufSize = 0, flags = MNT_NOWAIT;
179  int nextMount = 0;
180 
181  getmntinfo_r(&mounts, flags, &mntCount, &bufSize);
182 # elif defined(HAVE_GETMNTINFO)
183  /* This is Mac OS X */
184 #if defined(__NetBSD__)
185  struct statvfs * mounts = NULL;
186 #else
187  struct statfs * mounts = NULL;
188 #endif
189  int mntCount = 0, flags = MNT_NOWAIT;
190  int nextMount = 0;
191 
192  /* XXX 0 on error, errno set */
193  mntCount = getmntinfo(&mounts, flags);
194 # endif
195 
196  filesystems = xcalloc((numAlloced + 1), sizeof(*filesystems)); /* XXX memory leak */
197 
198  numFilesystems = 0;
199  while (1) {
200 # if GETMNTENT_ONE
201  /* this is Linux */
202  /*@-modunconnomods -moduncon @*/
203  our_mntent * itemptr = getmntent(mtab);
204  if (!itemptr) break;
205  item = *itemptr; /* structure assignment */
206  mntdir = item.our_mntdir;
207 #if defined(MNTOPT_RO)
208  /*@-compdef@*/
209  if (hasmntopt(itemptr, MNTOPT_RO) != NULL)
210  rdonly = 1;
211  /*@=compdef@*/
212 #endif
213  /*@=modunconnomods =moduncon @*/
214 # elif GETMNTENT_TWO
215  /* Solaris, maybe others */
216  if (getmntent(mtab, &item)) break;
217  mntdir = item.our_mntdir;
218 # elif defined(HAVE_GETMNTINFO_R)
219  /* This is OSF */
220  if (nextMount == mntCount) break;
221  mntdir = mounts[nextMount++].f_mntonname;
222 # elif defined(HAVE_GETMNTINFO)
223  /* This is Mac OS X */
224  if (nextMount == mntCount) break;
225  mntdir = mounts[nextMount++].f_mntonname;
226 # endif
227 
228 #if defined(RPM_VENDOR_OPENPKG) /* always-skip-proc-filesystem */
229  if (strcmp(mntdir, "/proc") == 0)
230  continue;
231 #endif
232 
233  if (Stat(mntdir, &sb) < 0) {
234  switch(errno) {
235  default:
236  rpmlog(RPMLOG_ERR, _("failed to stat %s: %s\n"), mntdir,
237  strerror(errno));
239  return 1;
240  /*@notreached@*/ /*@switchbreak@*/ break;
241  case ENOENT: /* XXX avoid /proc if leaked into *BSD jails. */
242  case EACCES: /* XXX fuse fs #220991 */
243  case ESTALE:
244  continue;
245  /*@notreached@*/ /*@switchbreak@*/ break;
246  }
247  }
248 
249  if ((numFilesystems + 2) == numAlloced) {
250  numAlloced += 10;
251  filesystems = xrealloc(filesystems,
252  sizeof(*filesystems) * (numAlloced + 1));
253  }
254 
255  filesystems[numFilesystems].dev = sb.st_dev;
256  filesystems[numFilesystems].mntPoint = xstrdup(mntdir);
257  filesystems[numFilesystems].rdonly = rdonly;
258 #if 0
259  rpmlog(RPMLOG_DEBUG, _("%5d 0x%04x %s %s\n"),
261  (unsigned) filesystems[numFilesystems].dev,
262  (filesystems[numFilesystems].rdonly ? "ro" : "rw"),
263  filesystems[numFilesystems].mntPoint);
264 #endif
265  numFilesystems++;
266  }
267 
268 # if GETMNTENT_ONE || GETMNTENT_TWO
269  (void) fclose(mtab);
270 # elif defined(HAVE_GETMNTINFO_R)
271  mounts = _free(mounts);
272 # endif
273 
274  filesystems[numFilesystems].dev = 0;
275  filesystems[numFilesystems].mntPoint = NULL;
276  filesystems[numFilesystems].rdonly = 0;
277 
278  fsnames = xcalloc((numFilesystems + 1), sizeof(*fsnames));
279  for (i = 0; i < numFilesystems; i++)
280  fsnames[i] = filesystems[i].mntPoint;
281  fsnames[numFilesystems] = NULL;
282 
283 /*@-nullstate@*/ /* FIX: fsnames[] may be NULL */
284  return 0;
285 /*@=nullstate@*/
286 }
287 #endif /* HAVE_MNTCTL */
288 
289 int rpmGetFilesystemList(const char *** listptr, rpmuint32_t * num)
290 {
291  if (!fsnames)
292  if (getFilesystemList())
293  return 1;
294 
295  if (listptr) *listptr = fsnames;
296  if (num) *num = numFilesystems;
297 
298  return 0;
299 }
300 
301 int rpmGetFilesystemUsage(const char ** fileList, rpmuint32_t * fssizes,
302  int numFiles, rpmuint64_t ** usagesPtr,
303  /*@unused@*/ int flags)
304 {
305  rpmuint64_t * usages;
306  int i, j;
307  char * buf, * dirName;
308  char * chptr;
309  size_t maxLen;
310  size_t len;
311  char * lastDir;
312  const char * sourceDir;
313  int lastfs = 0;
314  dev_t lastDev = (dev_t)-1; /* I hope nobody uses -1 for a st_dev */
315  struct stat sb;
316  int rc = 1; /* assume failure */
317 
318  if (!fsnames)
319  if (getFilesystemList())
320  return rc;
321 
322  usages = xcalloc(numFilesystems, sizeof(*usages));
323 
324  sourceDir = rpmGetPath("%{_sourcedir}", NULL);
325 
326  maxLen = strlen(sourceDir);
327  for (i = 0; i < numFiles; i++) {
328  len = strlen(fileList[i]);
329  if (maxLen < len) maxLen = len;
330  }
331 
332  buf = alloca(maxLen + 1);
333  lastDir = alloca(maxLen + 1);
334  dirName = alloca(maxLen + 1);
335  *lastDir = '\0';
336 
337  /* cut off last filename */
338  for (i = 0; i < numFiles; i++) {
339  if (*fileList[i] == '/') {
340  strcpy(buf, fileList[i]);
341  chptr = buf + strlen(buf) - 1;
342  while (*chptr != '/') chptr--;
343  if (chptr == buf)
344  buf[1] = '\0';
345  else
346  *chptr-- = '\0';
347  } else {
348  /* this should only happen for source packages (gulp) */
349  strcpy(buf, sourceDir);
350  }
351 
352  if (strcmp(lastDir, buf)) {
353  strcpy(dirName, buf);
354  chptr = dirName + strlen(dirName) - 1;
355  while (Stat(dirName, &sb) < 0) {
356  switch(errno) {
357  default:
358  rpmlog(RPMLOG_ERR, _("failed to stat %s: %s\n"), buf,
359  strerror(errno));
360  goto exit;
361  /*@notreached@*/ /*@switchbreak@*/ break;
362  case ENOENT: /* XXX paths in empty chroot's don't exist. */
363  /*@switchbreak@*/ break;
364  }
365 
366  /* cut off last directory part, because it was not found. */
367  while (*chptr != '/') chptr--;
368 
369  if (chptr == dirName)
370  dirName[1] = '\0';
371  else
372  *chptr-- = '\0';
373  }
374 
375  if (lastDev != sb.st_dev) {
376  for (j = 0; j < numFilesystems; j++)
377  if (filesystems && filesystems[j].dev == sb.st_dev)
378  /*@innerbreak@*/ break;
379 
380  if (j == numFilesystems) {
382  _("file %s is on an unknown device\n"), buf);
383  goto exit;
384  }
385 
386  lastfs = j;
387  lastDev = sb.st_dev;
388  }
389  }
390 
391  strcpy(lastDir, buf);
392  usages[lastfs] += fssizes[i];
393  }
394  rc = 0;
395 
396 exit:
397  sourceDir = _free(sourceDir);
398 
399  if (rc == 0 && usagesPtr)
400  *usagesPtr = usages;
401  else
402  usages = _free(usages);
403 
404  return rc;
405 }
406 /*@=usereleased =onlytrans@*/