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