filesystem.cc

Go to the documentation of this file.
00001 /**
00002  *  @file
00003  *  Implementation of the sefs_filesystem class.
00004  *
00005  *  @author Jeremy A. Mowery jmowery@tresys.com
00006  *  @author Jason Tang jtang@tresys.com
00007  *
00008  *  Copyright (C) 2007 Tresys Technology, LLC
00009  *
00010  *  This library is free software; you can redistribute it and/or
00011  *  modify it under the terms of the GNU Lesser General Public
00012  *  License as published by the Free Software Foundation; either
00013  *  version 2.1 of the License, or (at your option) any later version.
00014  *
00015  *  This library is distributed in the hope that it will be useful,
00016  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018  *  Lesser General Public License for more details.
00019  *
00020  *  You should have received a copy of the GNU Lesser General Public
00021  *  License along with this library; if not, write to the Free Software
00022  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00023  */
00024 
00025 #include <config.h>
00026 
00027 #include "sefs_internal.hh"
00028 #include "new_ftw.h"
00029 
00030 #include <sefs/entry.hh>
00031 #include <sefs/filesystem.hh>
00032 #include <apol/util.h>
00033 #include <selinux/context.h>
00034 #include <selinux/selinux.h>
00035 #include <assert.h>
00036 #include <errno.h>
00037 #include <mntent.h>
00038 #include <regex.h>
00039 #include <stdio.h>
00040 #include <string.h>
00041 #include <unistd.h>
00042 #include <sys/stat.h>
00043 #include <sys/types.h>
00044 
00045 extern int lgetfilecon_raw(const char *, security_context_t *) __attribute__ ((weak));
00046 
00047 /**
00048  * As that setools must work with older libselinux versions that may
00049  * not have the _raw() functions, declare them as weak.  If libselinux
00050  * does indeed have the new functions then use them; otherwise
00051  * fallback to the originals.
00052  */
00053 static int filesystem_lgetfilecon(const char *path, security_context_t * context)
00054 {
00055         if (lgetfilecon_raw != NULL)
00056         {
00057                 return lgetfilecon_raw(path, context);
00058         }
00059         else
00060         {
00061                 return lgetfilecon(path, context);
00062         }
00063 }
00064 
00065 #if 0
00066 
00067 /**
00068  * Given a directory, find all bounded mounted filesystems within that
00069  * directory (or subdirectory within.)  This function consults the
00070  * entries written to /etc/mtab to determine if something is mounted
00071  * or not and if it has the "bind" option.  Note that if \a dir itself
00072  * is a mount, it will not be reported; a subdirectory might.
00073  *
00074  * Note that the returned vector in never actually used by this
00075  * library.  This function existed in previous versions of libsefs,
00076  * but was never documented why it existed.  Rather than eliminate
00077  * this function, it is retained (but effectively unused), in case a
00078  * future revision of libsefs necessitates finding bind mounts.
00079  *
00080  * @param dir Directory to begin search.
00081  *
00082  * @return An allocated vector containing pathnames (type char *) to
00083  * each mounted location with the "bind" option.  The caller is
00084  * responsible for calling apol_vector_destroy() afterwards.
00085  */
00086 static apol_vector_t *filesystem_find_mount_points(const char *dir) throw(std::bad_alloc, std::runtime_error)
00087 {
00088         char *dirdup = NULL;
00089         apol_vector_t *v = NULL;
00090         FILE *mtab = NULL;
00091         struct mntent *entry;
00092 
00093         try
00094         {
00095                 if ((dirdup = strdup(dir)) == NULL)
00096                 {
00097                         throw std::bad_alloc();
00098                 }
00099                 size_t len = strlen(dirdup);
00100                 if (len > 1 && dirdup[len - 1] == '/')
00101                 {
00102                         dirdup[len - 1] = '\0';
00103                 }
00104                 if ((v = apol_vector_create(free)) == NULL)
00105                 {
00106                         throw std::bad_alloc();
00107                 }
00108                 if ((mtab = fopen("/etc/mtab", "r")) == NULL)
00109                 {
00110                         throw std::runtime_error(strerror(errno));
00111                 }
00112                 // note non thread-safeness below
00113                 while ((entry = getmntent(mtab)) != NULL)
00114                 {
00115                         if (strstr(entry->mnt_dir, dir) != entry->mnt_dir)
00116                         {
00117                                 continue;
00118                         }
00119                         if (strcmp(entry->mnt_dir, dirdup) == 0)
00120                         {
00121                                 continue;
00122                         }
00123                         if (strstr(entry->mnt_opts, "bind") != NULL)
00124                         {
00125                                 char *s = strdup(entry->mnt_dir);
00126                                 if (s == NULL)
00127                                 {
00128                                         throw std::bad_alloc();
00129                                 }
00130                                 if (apol_vector_append(v, s) < 0)
00131                                 {
00132                                         free(s);
00133                                         throw std::bad_alloc();
00134                                 }
00135                         }
00136                 }
00137 
00138         }
00139         catch(...)
00140         {
00141                 free(dirdup);
00142                 apol_vector_destroy(&v);
00143                 if (mtab != NULL)
00144                 {
00145                         fclose(mtab);
00146                 }
00147                 throw;
00148         }
00149         free(dirdup);
00150         fclose(mtab);
00151         return v;
00152 }
00153 
00154 #endif
00155 
00156 /******************** public functions below ********************/
00157 
00158 sefs_filesystem::sefs_filesystem(const char *new_root, sefs_callback_fn_t msg_callback, void *varg)throw(std::bad_alloc, std::invalid_argument, std::runtime_error):sefs_fclist(SEFS_FCLIST_TYPE_FILESYSTEM,
00159             msg_callback,
00160             varg)
00161 {
00162         if (new_root == NULL)
00163         {
00164                 SEFS_ERR(this, "%s", strerror(EINVAL));
00165                 errno = EINVAL;
00166                 throw std::invalid_argument(strerror(EINVAL));
00167         }
00168         _root = NULL;
00169         _mls = false;
00170         try
00171         {
00172                 // check that root exists and is readable
00173                 struct stat64 sb;
00174                 if (stat64(new_root, &sb) != 0 && !S_ISDIR(sb.st_mode))
00175                 {
00176                         SEFS_ERR(this, "%s", strerror(EINVAL));
00177                         errno = EINVAL;
00178                         throw std::invalid_argument(strerror(EINVAL));
00179                 }
00180 
00181                 // determine if filesystem is MLS or not
00182                 security_context_t scon;
00183                 if (filesystem_lgetfilecon(new_root, &scon) < 0)
00184                 {
00185                         SEFS_ERR(this, "Could not read SELinux file context for %s.", new_root);
00186                         throw std::runtime_error(strerror(errno));
00187                 }
00188                 context_t con;
00189                 if ((con = context_new(scon)) == 0)
00190                 {
00191                         SEFS_ERR(this, "%s", strerror(errno));
00192                         freecon(scon);
00193                         throw std::runtime_error(strerror(errno));
00194                 }
00195                 freecon(scon);
00196                 const char *range = context_range_get(con);
00197                 if (range != NULL && range[0] != '\0')
00198                 {
00199                         _mls = true;
00200                 }
00201                 context_free(con);
00202 
00203                 if ((_root = strdup(new_root)) == NULL)
00204                 {
00205                         SEFS_ERR(this, "%s", strerror(errno));
00206                         throw std::bad_alloc();
00207                 }
00208         }
00209         catch(...)
00210         {
00211                 free(_root);
00212                 throw;
00213         }
00214 }
00215 
00216 sefs_filesystem::~sefs_filesystem()
00217 {
00218         free(_root);
00219 }
00220 
00221 struct filesystem_ftw_struct
00222 {
00223         sefs_filesystem *fs;
00224         sefs_query *query;
00225         apol_vector_t *dev_map;        //< vector of filesystem_dev entries
00226         apol_vector_t *type_list;
00227         apol_mls_range_t *range;
00228         sefs_fclist_map_fn_t fn;
00229         void *data;
00230         bool aborted;
00231         int retval;
00232 };
00233 
00234 // wrapper functions to go between non-OO land into OO member functions
00235 
00236 inline struct sefs_context_node *filesystem_get_context(sefs_filesystem * fs, security_context_t scon) throw(std::bad_alloc)
00237 {
00238         return fs->getContext(scon);
00239 }
00240 
00241 inline sefs_entry *filesystem_get_entry(sefs_filesystem * fs, const struct sefs_context_node * node, uint32_t objClass,
00242                                         const char *path, ino64_t ino, const char *dev_name)throw(std::bad_alloc)
00243 {
00244         return fs->getEntry(node, objClass, path, ino, dev_name);
00245 }
00246 
00247 inline bool filesystem_is_query_match(sefs_filesystem * fs, const sefs_query * query, const char *path, const char *dev,
00248                                       const struct stat64 * sb, apol_vector_t * type_list,
00249                                       apol_mls_range_t * range)throw(std::runtime_error)
00250 {
00251         return fs->isQueryMatch(query, path, dev, sb, type_list, range);
00252 }
00253 
00254 static uint32_t filesystem_stat_to_objclass(const struct stat64 *sb)
00255 {
00256         if (S_ISREG(sb->st_mode))
00257         {
00258                 return QPOL_CLASS_FILE;
00259         }
00260         if (S_ISDIR(sb->st_mode))
00261         {
00262                 return QPOL_CLASS_DIR;
00263         }
00264         if (S_ISCHR(sb->st_mode))
00265         {
00266                 return QPOL_CLASS_CHR_FILE;
00267         }
00268         if (S_ISBLK(sb->st_mode))
00269         {
00270                 return QPOL_CLASS_BLK_FILE;
00271         }
00272         if (S_ISFIFO(sb->st_mode))
00273         {
00274                 return QPOL_CLASS_FIFO_FILE;
00275         }
00276         if (S_ISLNK(sb->st_mode))
00277         {
00278                 return QPOL_CLASS_LNK_FILE;
00279         }
00280         if (S_ISSOCK(sb->st_mode))
00281         {
00282                 return QPOL_CLASS_SOCK_FILE;
00283         }
00284         assert(0);                     // should never get here
00285         return 0;
00286 }
00287 
00288 struct filesystem_dev
00289 {
00290         dev_t dev;
00291         char *dev_name;                //< pointer into the dev_tree
00292 };
00293 
00294 static int filesystem_dev_cmp(const void *a, const void *b __attribute__ ((unused)), void *arg)
00295 {
00296         const struct filesystem_dev *d1 = static_cast < const struct filesystem_dev *>(a);
00297         dev_t *d2 = static_cast < dev_t * >(arg);
00298         if (d1->dev < *d2)
00299         {
00300                 return -1;
00301         }
00302         else if (d1->dev > *d2)
00303         {
00304                 return 1;
00305         }
00306         return 0;
00307 }
00308 
00309 static int filesystem_ftw_handler(const char *fpath, const struct stat64 *sb, int typeflag
00310                                   __attribute__ ((unused)), struct FTW *ftwbuf __attribute__ ((unused)), void *data)
00311 {
00312         struct filesystem_ftw_struct *s = static_cast < struct filesystem_ftw_struct *>(data);
00313 
00314         size_t i;
00315         void *dev_num = const_cast < void *>(static_cast < const void *>(&(sb->st_dev)));
00316         int rc = apol_vector_get_index(s->dev_map, NULL, filesystem_dev_cmp, dev_num, &i);
00317         const char *dev = "<unknown>";
00318         if (rc == 0)
00319         {
00320                 // if the device number was discovered in buildDevMap
00321                 // then store the device name within the entry
00322                 struct filesystem_dev *d = static_cast < struct filesystem_dev *>(apol_vector_get_element(s->dev_map, i));
00323                 dev = d->dev_name;
00324         }
00325         else
00326         {
00327                 SEFS_WARN(s->fs, "Unknown device for %s.", fpath);
00328         }
00329         try
00330         {
00331                 if (!filesystem_is_query_match(s->fs, s->query, fpath, dev, sb, s->type_list, s->range))
00332                 {
00333                         return 0;
00334                 }
00335         }
00336         catch(...)
00337         {
00338                 return -1;
00339         }
00340 
00341         security_context_t scon;
00342         if (filesystem_lgetfilecon(fpath, &scon) < 0)
00343         {
00344                 SEFS_ERR(s->fs, "Could not read SELinux file context for %s.", fpath);
00345                 return -1;
00346         }
00347         struct sefs_context_node *node = NULL;
00348         try
00349         {
00350                 node = filesystem_get_context(s->fs, scon);
00351         }
00352         catch(...)
00353         {
00354                 freecon(scon);
00355                 return -1;
00356         }
00357         freecon(scon);
00358 
00359         uint32_t objClass = filesystem_stat_to_objclass(sb);
00360 
00361         sefs_entry *entry = NULL;
00362         try
00363         {
00364                 entry = filesystem_get_entry(s->fs, node, objClass, fpath, sb->st_ino, dev);
00365         }
00366         catch(...)
00367         {
00368                 return -1;
00369         }
00370 
00371         // invoke real callback (not just the nftw handler)
00372         s->retval = s->fn(s->fs, entry, s->data);
00373         delete entry;
00374         if (s->retval < 0)
00375         {
00376                 s->aborted = true;
00377                 return s->retval;
00378         }
00379 
00380         return 0;
00381 }
00382 
00383 int sefs_filesystem::runQueryMap(sefs_query * query, sefs_fclist_map_fn_t fn, void *data) throw(std::runtime_error,
00384                                                                                                 std::invalid_argument)
00385 {
00386         struct filesystem_ftw_struct s;
00387         s.dev_map = NULL;
00388         s.type_list = NULL;
00389         s.range = NULL;
00390         try
00391         {
00392                 s.dev_map = buildDevMap();
00393                 if (query != NULL)
00394                 {
00395                         query->compile();
00396                         if (policy != NULL)
00397                         {
00398                                 if (query->_type != NULL && query->_indirect &&
00399                                     (s.type_list =
00400                                      query_create_candidate_type(policy, query->_type, query->_retype, query->_regex,
00401                                                                  query->_indirect)) == NULL)
00402                                 {
00403                                         SEFS_ERR(this, "%s", strerror(errno));
00404                                         throw std::runtime_error(strerror(errno));
00405                                 }
00406                                 if (query->_range != NULL && query->_rangeMatch != 0 &&
00407                                     (s.range = apol_mls_range_create_from_string(policy, query->_range)) == NULL)
00408                                 {
00409                                         SEFS_ERR(this, "%s", strerror(errno));
00410                                         throw std::runtime_error(strerror(errno));
00411                                 }
00412                         }
00413                 }
00414         }
00415         catch(...)
00416         {
00417                 apol_vector_destroy(&s.dev_map);
00418                 apol_vector_destroy(&s.type_list);
00419                 apol_mls_range_destroy(&s.range);
00420                 throw;
00421         }
00422         s.fs = this;
00423         s.query = query;
00424         s.fn = fn;
00425         s.data = data;
00426         s.aborted = false;
00427         s.retval = 0;
00428 
00429         int retval = new_nftw64(_root, filesystem_ftw_handler, 1024, 0, &s);
00430         apol_vector_destroy(&s.dev_map);
00431         apol_vector_destroy(&s.type_list);
00432         apol_mls_range_destroy(&s.range);
00433         if (retval != 0 && !s.aborted)
00434         {
00435                 // error was generated by new_nftw64() itself, not
00436                 // from callback
00437                 return retval;
00438         }
00439         return s.retval;
00440 }
00441 
00442 bool sefs_filesystem::isMLS() const
00443 {
00444         return _mls;
00445 }
00446 
00447 const char *sefs_filesystem::root() const
00448 {
00449         return _root;
00450 }
00451 
00452 /******************** private functions below ********************/
00453 
00454 static void filesystem_dev_free(void *elem)
00455 {
00456         if (elem != NULL)
00457         {
00458                 struct filesystem_dev *d = static_cast < struct filesystem_dev *>(elem);
00459                 // don't free the device name pointer, because it's pointing
00460                 // into the dev_tree BST
00461                 free(d);
00462         }
00463 }
00464 
00465 const char *sefs_filesystem::getDevName(const dev_t dev) throw(std::runtime_error)
00466 {
00467         apol_vector_t *dev_map = buildDevMap();
00468         size_t i;
00469         void *devp = const_cast < dev_t * >(&dev);
00470         int rc = apol_vector_get_index(dev_map, NULL, filesystem_dev_cmp, devp, &i);
00471         if (rc < 0)
00472         {
00473                 apol_vector_destroy(&dev_map);
00474                 return NULL;
00475         }
00476         struct filesystem_dev *d = static_cast < struct filesystem_dev *>(apol_vector_get_element(dev_map, i));
00477         const char *dev_name = d->dev_name;     // this is pointing into this->_dev_tree
00478         apol_vector_destroy(&dev_map);
00479         return dev_name;
00480 }
00481 
00482 /**
00483  * For each entry in /etc/mtab, record the device number and the name
00484  * of the mounted file system.  This provides the mapping between a
00485  * device number and its source device.
00486  *
00487  * @return Vector of filesystem_dev entries.  The caller must call
00488  * apol_vector_destroy() upon the vector afterwards.
00489  * @exception If error allocating space, unable to open /etc/mtab, or
00490  * unable to parse mtab file.
00491  */
00492 apol_vector_t *sefs_filesystem::buildDevMap(void)throw(std::runtime_error)
00493 {
00494         apol_vector_t *dev_map;
00495         if ((dev_map = apol_vector_create(filesystem_dev_free)) == NULL)
00496         {
00497                 SEFS_ERR(this, "%s", strerror(errno));
00498                 throw std::runtime_error(strerror(errno));
00499         }
00500         FILE *f = NULL;
00501         try
00502         {
00503                 if ((f = fopen("/etc/mtab", "r")) == NULL)
00504                 {
00505                         SEFS_ERR(this, "%s", strerror(errno));
00506                         throw std::runtime_error(strerror(errno));
00507                 }
00508                 char buf[256];
00509                 struct mntent mntbuf;
00510                 while (getmntent_r(f, &mntbuf, buf, 256) != NULL)
00511                 {
00512                         struct stat sb;
00513                         if (stat(mntbuf.mnt_dir, &sb) == -1)
00514                         {
00515                                 // could not open this device, so skip
00516                                 // it (and hope it won't be examined
00517                                 // during runQuery())
00518                                 continue;
00519                         }
00520                         else
00521                         {
00522                                 struct filesystem_dev *d = static_cast < struct filesystem_dev *>(calloc(1, sizeof(*d)));
00523                                 if (d == NULL)
00524                                 {
00525                                         SEFS_ERR(this, "%s", strerror(errno));
00526                                         throw std::runtime_error(strerror(errno));
00527                                 }
00528                                 if (apol_vector_append(dev_map, d) < 0)
00529                                 {
00530                                         SEFS_ERR(this, "%s", strerror(errno));
00531                                         filesystem_dev_free(d);
00532                                         throw std::runtime_error(strerror(errno));
00533                                 }
00534                                 d->dev = sb.st_dev;
00535                                 char *mnt_fsname = strdup(mntbuf.mnt_fsname);
00536                                 if (mnt_fsname == NULL)
00537                                 {
00538                                         SEFS_ERR(this, "%s", strerror(errno));
00539                                         throw std::runtime_error(strerror(errno));
00540                                 }
00541                                 if (apol_bst_insert_and_get(dev_tree, (void **)&mnt_fsname, NULL) < 0)
00542                                 {
00543                                         SEFS_ERR(this, "%s", strerror(errno));
00544                                         free(mnt_fsname);
00545                                         throw std::runtime_error(strerror(errno));
00546                                 }
00547                                 d->dev_name = mnt_fsname;
00548                         }
00549                 }
00550         }
00551         catch(...)
00552         {
00553                 apol_vector_destroy(&dev_map);
00554                 if (f != NULL)
00555                 {
00556                         fclose(f);
00557                 }
00558                 throw;
00559         }
00560         fclose(f);
00561         return dev_map;
00562 }
00563 
00564 bool sefs_filesystem::isQueryMatch(const sefs_query * query, const char *path, const char *dev, const struct stat64 * sb,
00565                                    apol_vector_t * type_list, apol_mls_range_t * range)throw(std::runtime_error)
00566 {
00567         if (query == NULL)
00568         {
00569                 return true;
00570         }
00571         security_context_t scon;
00572         if (filesystem_lgetfilecon(path, &scon) < 0)
00573         {
00574                 SEFS_ERR(this, "%s", strerror(errno));
00575                 throw std::runtime_error(strerror(errno));
00576         }
00577         context_t con;
00578         if ((con = context_new(scon)) == 0)
00579         {
00580                 SEFS_ERR(this, "%s", strerror(errno));
00581                 freecon(scon);
00582                 throw std::runtime_error(strerror(errno));
00583         }
00584         freecon(scon);
00585 
00586         if (!query_str_compare(context_user_get(con), query->_user, query->_reuser, query->_regex))
00587         {
00588                 context_free(con);
00589                 return false;
00590         }
00591         if (!query_str_compare(context_role_get(con), query->_role, query->_rerole, query->_regex))
00592         {
00593                 context_free(con);
00594                 return false;
00595         }
00596 
00597         bool str_matched = false, pol_matched = false;
00598         str_matched = query_str_compare(context_type_get(con), query->_type, query->_retype, query->_regex);
00599         if (type_list != NULL && !str_matched)
00600         {
00601                 size_t index;
00602                 pol_matched = (apol_vector_get_index(type_list, context_type_get(con), apol_str_strcmp, NULL, &index) < 0);
00603         }
00604         if (!str_matched && !pol_matched)
00605         {
00606                 context_free(con);
00607                 return false;
00608         }
00609 
00610         if (isMLS())
00611         {
00612                 if (range == NULL)
00613                 {
00614                         if (!query_str_compare(context_range_get(con), query->_range, query->_rerange, query->_regex))
00615                         {
00616                                 context_free(con);
00617                                 return false;
00618                         }
00619                 }
00620                 else
00621                 {
00622                         assert(policy != NULL);
00623                         apol_mls_range_t *context_range = apol_mls_range_create_from_string(policy, context_range_get(con));
00624                         if (context_range == NULL)
00625                         {
00626                                 SEFS_ERR(this, "%s", strerror(errno));
00627                                 context_free(con);
00628                                 throw std::runtime_error(strerror(errno));
00629                         }
00630                         int ret;
00631                         ret = apol_mls_range_compare(policy, range, context_range, query->_rangeMatch);
00632                         apol_mls_range_destroy(&context_range);
00633                         if (ret <= 0)
00634                         {
00635                                 context_free(con);
00636                                 return false;
00637                         }
00638                 }
00639         }
00640 
00641         context_free(con);
00642 
00643         if (query->_objclass != 0 && query->_objclass != filesystem_stat_to_objclass(sb))
00644         {
00645                 return false;
00646         }
00647 
00648         if (!query_str_compare(path, query->_path, query->_repath, query->_regex))
00649         {
00650                 return false;
00651         }
00652 
00653         if (query->_inode != 0 && query->_inode != sb->st_ino)
00654         {
00655                 return false;
00656         }
00657 
00658         if (!query_str_compare(dev, query->_dev, query->_redev, query->_regex))
00659         {
00660                 return false;
00661         }
00662 
00663         return true;
00664 }
00665 
00666 sefs_entry *sefs_filesystem::getEntry(const struct sefs_context_node * context, uint32_t objectClass,
00667                                       const char *path, ino64_t ino, const char *dev_name)throw(std::bad_alloc)
00668 {
00669         char *s = strdup(path);
00670         if (s == NULL)
00671         {
00672                 SEFS_ERR(this, "%s", strerror(errno));
00673                 throw std::bad_alloc();
00674         }
00675         if (apol_bst_insert_and_get(path_tree, (void **)&s, NULL) < 0)
00676         {
00677                 SEFS_ERR(this, "%s", strerror(errno));
00678                 free(s);
00679                 throw std::bad_alloc();
00680         }
00681         sefs_entry *e = new sefs_entry(this, context, objectClass, s);
00682         e->_inode = ino;
00683         e->_dev = dev_name;
00684         return e;
00685 }
00686 
00687 /******************** C functions below ********************/
00688 
00689 sefs_filesystem_t *sefs_filesystem_create(const char *root, sefs_callback_fn_t msg_callback, void *varg)
00690 {
00691         sefs_filesystem_t *fs;
00692         try
00693         {
00694                 fs = new sefs_filesystem(root, msg_callback, varg);
00695         }
00696         catch(...)
00697         {
00698                 errno = ENOMEM;
00699                 return NULL;
00700         }
00701         return fs;
00702 }
00703 
00704 const char *sefs_filesystem_get_root(const sefs_filesystem_t * fs)
00705 {
00706         if (fs == NULL)
00707         {
00708                 SEFS_ERR(NULL, "%s", strerror(EINVAL));
00709                 errno = EINVAL;
00710                 return NULL;
00711         }
00712         return fs->root();
00713 }
00714 
00715 extern const char *sefs_filesystem_get_dev_name(sefs_filesystem_t * fs, const dev_t dev)
00716 {
00717         if (fs == NULL)
00718         {
00719                 SEFS_ERR(NULL, "%s", strerror(EINVAL));
00720                 errno = EINVAL;
00721                 return NULL;
00722         }
00723         const char *dev_name = NULL;
00724         try
00725         {
00726                 dev_name = fs->getDevName(dev);
00727         }
00728         catch(...)
00729         {
00730                 return NULL;
00731         }
00732         return dev_name;
00733 }