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