]> git.ipfire.org Git - pakfire.git/commitdiff
linter: Parse the ELF file early to avoid this callback hell
authorMichael Tremer <michael.tremer@ipfire.org>
Sat, 26 Oct 2024 18:12:39 +0000 (18:12 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Sat, 26 Oct 2024 18:14:03 +0000 (18:14 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/linter-file.c
src/libpakfire/linter.c

index 6dd8dfef5afb2a0bc1506da8b07daac1b53df477..ee39d6143360d5acd00004e60fdd510e27a82b85 100644 (file)
@@ -54,6 +54,9 @@ struct pakfire_linter_file {
 
        // Path
        const char* path;
+
+       // ELF
+       Elf* elf;
 };
 
 #define pakfire_linter_file_info(lfile, format, ...) \
@@ -63,6 +66,32 @@ struct pakfire_linter_file {
 #define pakfire_linter_file_error(lfile, format, ...) \
        pakfire_linter_result(lfile->linter, lfile->file, PAKFIRE_LINTER_ERROR, format, ## __VA_ARGS__)
 
+static int pakfire_linter_file_init_libelf(struct pakfire_linter_file* lfile) {
+       // Initialize libelf
+       if (elf_version(EV_CURRENT) == EV_NONE) {
+               ERROR(lfile->ctx, "Could not initialize libelf: %s\n", elf_errmsg(-1));
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int pakfire_linter_file_open_elf(struct pakfire_linter_file* lfile) {
+       int r;
+
+       // Initialize libelf
+       r = pakfire_linter_file_init_libelf(lfile);
+       if (r < 0)
+               return r;
+
+       // Parse the ELF header
+       lfile->elf = elf_memory(lfile->data, lfile->length);
+       if (!lfile->elf)
+               return -errno;
+
+       return 0;
+}
+
 /*
        Maps the file into memory
 */
@@ -128,6 +157,13 @@ int pakfire_linter_file_create(struct pakfire_linter_file** lfile,
                goto ERROR;
        }
 
+       // Initialize libelf
+       r = pakfire_linter_file_open_elf(l);
+       if (r < 0) {
+               ERROR(l->ctx, "Could not initialize libelf: %s\n", strerror(-r));
+               goto ERROR;
+       }
+
        // Return the pointer
        *lfile = pakfire_linter_file_ref(l);
 
@@ -141,20 +177,25 @@ ERROR:
 static void pakfire_linter_file_free(struct pakfire_linter_file* lfile) {
        int r;
 
+       if (lfile->elf)
+               elf_end(lfile->elf);
+
        if (lfile->data) {
                r = munmap(lfile->data, lfile->length);
                if (r < 0)
                        ERROR(lfile->ctx, "Could not unmmap %s: %m\n", lfile->path);
        }
 
+       if (lfile->fd >= 0)
+               close(lfile->fd);
+
        if (lfile->linter)
                pakfire_linter_unref(lfile->linter);
        if (lfile->file)
                pakfire_file_unref(lfile->file);
        if (lfile->ctx)
                pakfire_ctx_unref(lfile->ctx);
-       if (lfile->fd >= 0)
-               close(lfile->fd);
+       free(lfile);
 }
 
 struct pakfire_linter_file* pakfire_linter_file_ref(struct pakfire_linter_file* lfile) {
@@ -284,128 +325,45 @@ static int pakfire_linter_check_script_interpreter(struct pakfire_linter_file* l
        return 0;
 }
 
-static int pakfire_linter_file_init_libelf(struct pakfire_linter_file* lfile) {
-       // Initialize libelf
-       if (elf_version(EV_CURRENT) == EV_NONE) {
-               ERROR(lfile->ctx, "Could not initialize libelf: %s\n", elf_errmsg(-1));
-               return -EINVAL;
-       }
-
-       return 0;
-}
 
 static int pakfire_linter_file_is_elf(struct pakfire_linter_file* lfile) {
-       Elf* elf = NULL;
-       int r;
-
-       // Initialize libelf
-       r = pakfire_linter_file_init_libelf(lfile);
-       if (r < 0)
-               return r;
-
-       // Parse the ELF header
-       elf = elf_begin(lfile->fd, ELF_C_READ, NULL);
-       if (!elf) {
-               ERROR(lfile->ctx, "Could not open ELF file: %s\n", elf_errmsg(-1));
-               r = -EINVAL;
-               goto ERROR;
-       }
+       // Don't know if we don't have an ELF object
+       if (!lfile->elf)
+               return -EINVAL;
 
-       switch (elf_kind(elf)) {
+       switch (elf_kind(lfile->elf)) {
                case ELF_K_ELF:
-                       r = 1;
-                       break;
+                       return 1;
 
                // Ignore everything else
                default:
-                       r = 0;
                        break;
        }
 
-ERROR:
-       if (elf)
-               elf_end(elf);
-
-       return r;
-}
-
-/*
-       A helper function that opens an ELF file and calls a callback.
-*/
-static int pakfire_linter_file_elf(struct pakfire_linter_file* lfile,
-               int (*callback)(struct pakfire_linter_file* lfile, Elf* elf, void* data), void* data) {
-       Elf* elf = NULL;
-       int r;
-
-       // Initialize libelf
-       r = pakfire_linter_file_init_libelf(lfile);
-       if (r < 0)
-               return r;
-
-       // Parse the ELF header
-       elf = elf_begin(lfile->fd, ELF_C_READ, NULL);
-       if (!elf) {
-               ERROR(lfile->ctx, "Could not open ELF file: %s\n", elf_errmsg(-1));
-               r = -EINVAL;
-               goto ERROR;
-       }
-
-       // Check if this is an ELF file
-       switch (elf_kind(elf)) {
-               case ELF_K_ELF:
-                       break;
-
-               default:
-                       ERROR(lfile->ctx, "%s is not an ELF object\n", lfile->path);
-                       r = -EINVAL;
-                       goto ERROR;
-       }
-
-       // Call the callback
-       r = callback(lfile, elf, data);
-
-ERROR:
-       if (elf)
-               elf_end(elf);
-
-       return r;
+       return 0;
 }
 
-static int __pakfire_linter_file_get_elf_type(
-               struct pakfire_linter_file* lfile, Elf* elf, void* data) {
+static int pakfire_linter_file_get_elf_type(struct pakfire_linter_file* lfile) {
        GElf_Ehdr ehdr = {};
 
-       int* type = data;
+       // Check if have an ELF object
+       if (lfile->elf)
+               return -EINVAL;
 
        // Fetch the ELF header
-       if (!gelf_getehdr(elf, &ehdr)) {
+       if (!gelf_getehdr(lfile->elf, &ehdr)) {
                ERROR(lfile->ctx, "Could not parse ELF header: %s\n", elf_errmsg(-1));
                return -EINVAL;
        }
 
-       // Store the type
-       *type = ehdr.e_type;
-
-       return 0;
-}
-
-static int pakfire_linter_file_get_elf_type(struct pakfire_linter_file* lfile) {
-       int type = ET_NONE;
-       int r;
-
-       // Fetch the type
-       r = pakfire_linter_file_elf(lfile, __pakfire_linter_file_get_elf_type, &type);
-       if (r < 0)
-               return r;
-
-       return type;
+       return ehdr.e_type;
 }
 
 typedef int (*__pakfire_linter_file_for_elf_section_callback)(struct pakfire_linter_file* lfile,
-       Elf* elf, const Elf_Scn* section, const GElf_Shdr* shdr, Elf_Data* data);
+       const Elf_Scn* section, const GElf_Shdr* shdr, Elf_Data* data);
 
 static int pakfire_linter_file_for_elf_section(struct pakfire_linter_file* lfile,
-               Elf* elf, const Elf64_Word type, __pakfire_linter_file_for_elf_section_callback callback) {
+               const Elf64_Word type, __pakfire_linter_file_for_elf_section_callback callback) {
        Elf_Scn* section = NULL;
        GElf_Shdr shdr;
        Elf_Data* data;
@@ -413,7 +371,7 @@ static int pakfire_linter_file_for_elf_section(struct pakfire_linter_file* lfile
 
        // Walk through all sections
        for (;;) {
-               section = elf_nextscn(elf, section);
+               section = elf_nextscn(lfile->elf, section);
                if (!section)
                        break;
 
@@ -428,7 +386,7 @@ static int pakfire_linter_file_for_elf_section(struct pakfire_linter_file* lfile
                data = elf_getdata(section, NULL);
 
                // Call the callback
-               r = callback(lfile, elf, section, &shdr, data);
+               r = callback(lfile, section, &shdr, data);
                if (r)
                        break;
        }
@@ -438,14 +396,13 @@ static int pakfire_linter_file_for_elf_section(struct pakfire_linter_file* lfile
 
 
 static int pakfire_linter_file_get_elf_section(struct pakfire_linter_file* lfile,
-               Elf* elf, const Elf64_Word type, Elf_Scn** section, GElf_Shdr* header, Elf_Data** data) {
+               const Elf64_Word type, Elf_Scn** section, GElf_Shdr* header, Elf_Data** data) {
        Elf_Scn* s = NULL;
-
        GElf_Shdr shdr;
 
        // Walk through all sections
        for (;;) {
-               s = elf_nextscn(elf, s);
+               s = elf_nextscn(lfile->elf, s);
                if (!s)
                        break;
 
@@ -472,8 +429,8 @@ static int pakfire_linter_file_get_elf_section(struct pakfire_linter_file* lfile
        return 1;
 }
 
-static int pakfire_linter_file_elf_dyn_walk(struct pakfire_linter_file* lfile, Elf* elf,
-               int (*callback)(struct pakfire_linter_file* lfile, Elf* elf,
+static int pakfire_linter_file_elf_dyn_walk(struct pakfire_linter_file* lfile,
+               int (*callback)(struct pakfire_linter_file* lfile,
                        const GElf_Shdr* shdr, const GElf_Dyn* dyn, void* data), void* data) {
        Elf_Scn* dynamic = NULL;
        GElf_Shdr shdr;
@@ -482,7 +439,7 @@ static int pakfire_linter_file_elf_dyn_walk(struct pakfire_linter_file* lfile, E
        int r;
 
        // Find the dynamic linking information
-       r = pakfire_linter_file_get_elf_section(lfile, elf, SHT_DYNAMIC, &dynamic, &shdr, &elf_data);
+       r = pakfire_linter_file_get_elf_section(lfile, SHT_DYNAMIC, &dynamic, &shdr, &elf_data);
        if (r) {
                DEBUG(lfile->ctx, "%s does not have a dynamic section\n", lfile->path);
                return 0;
@@ -495,7 +452,7 @@ static int pakfire_linter_file_elf_dyn_walk(struct pakfire_linter_file* lfile, E
                        break;
 
                // Call the callback
-               r = callback(lfile, elf, &shdr, &dyn, data);
+               r = callback(lfile, &shdr, &dyn, data);
                if (r)
                        return r;
        }
@@ -503,7 +460,6 @@ static int pakfire_linter_file_elf_dyn_walk(struct pakfire_linter_file* lfile, E
        return 0;
 }
 
-
 static int pakfire_linter_file_check_pie(struct pakfire_linter_file* lfile) {
        switch (pakfire_linter_file_get_elf_type(lfile)) {
                // Shared Object files are good
@@ -518,8 +474,7 @@ static int pakfire_linter_file_check_pie(struct pakfire_linter_file* lfile) {
        return pakfire_linter_file_error(lfile, "Missing PIE");
 }
 
-static int __pakfire_linter_file_check_ssp(
-               struct pakfire_linter_file* lfile, Elf* elf, void* data) {
+static int pakfire_linter_file_check_ssp(struct pakfire_linter_file* lfile) {
        Elf_Scn* symtab = NULL;
        GElf_Shdr shdr;
        Elf_Data* elf_data = NULL;
@@ -527,8 +482,27 @@ static int __pakfire_linter_file_check_ssp(
        const char* name = NULL;
        int r;
 
+       // This check will be skipped for these files
+       static const char* whitelist[] = {
+               "/usr/lib64/libgcc_s.so.*",
+               "/usr/lib64/libmvec.so.*",
+               NULL,
+       };
+
+       // Do not perform this check for runtime linkers
+       if (pakfire_file_matches(lfile->file, "/usr/lib*/ld-*.so*"))
+               return 0;
+
+       // Check if this file is whitelisted
+       for (const char** path = whitelist; *path; path++) {
+               if (pakfire_file_matches(lfile->file, *path)) {
+                       DEBUG(lfile->ctx, "Skipping SSP check for whitelisted file %s\n", lfile->path);
+                       return 0;
+               }
+       }
+
        // Fetch the symbol table
-       r = pakfire_linter_file_get_elf_section(lfile, elf, SHT_SYMTAB, &symtab, &shdr, &elf_data);
+       r = pakfire_linter_file_get_elf_section(lfile, SHT_SYMTAB, &symtab, &shdr, &elf_data);
        if (r) {
                ERROR(lfile->ctx, "%s has no symbol table\n", lfile->path);
                return 1;
@@ -542,7 +516,7 @@ static int __pakfire_linter_file_check_ssp(
                gelf_getsym(elf_data, i, &symbol);
 
                // Fetch the symbol name
-               name = elf_strptr(elf, shdr.sh_link, symbol.st_name);
+               name = elf_strptr(lfile->elf, shdr.sh_link, symbol.st_name);
 
                // Skip empty section names
                if (!name || !*name)
@@ -577,38 +551,14 @@ static int __pakfire_linter_file_check_ssp(
        return pakfire_linter_file_error(lfile, "Missing Stack Smashing Protection");
 }
 
-static int pakfire_linter_file_check_ssp(struct pakfire_linter_file* lfile) {
-       // This check will be skipped for these files
-       static const char* whitelist[] = {
-               "/usr/lib64/libgcc_s.so.*",
-               "/usr/lib64/libmvec.so.*",
-               NULL,
-       };
-
-       // Do not perform this check for runtime linkers
-       if (pakfire_file_matches(lfile->file, "/usr/lib*/ld-*.so*"))
-               return 0;
-
-       // Check if this file is whitelisted
-       for (const char** path = whitelist; *path; path++) {
-               if (pakfire_file_matches(lfile->file, *path)) {
-                       DEBUG(lfile->ctx, "Skipping SSP check for whitelisted file %s\n", lfile->path);
-                       return 0;
-               }
-       }
-
-       return pakfire_linter_file_elf(lfile, __pakfire_linter_file_check_ssp, NULL);
-}
-
-static int __pakfire_linter_file_check_execstack(
-               struct pakfire_linter_file* lfile, Elf* elf, void* data) {
+static int pakfire_linter_file_check_execstack(struct pakfire_linter_file* lfile) {
        GElf_Phdr phdr;
        int r;
 
        size_t phnum = 0;
 
        // Fetch the total numbers of program headers
-       r = elf_getphdrnum(elf, &phnum);
+       r = elf_getphdrnum(lfile->elf, &phnum);
        if (r) {
                ERROR(lfile->ctx,
                        "Could not fetch number of program headers: %s\n", elf_errmsg(-1));
@@ -617,7 +567,7 @@ static int __pakfire_linter_file_check_execstack(
 
        // Walk through all program headers
        for (unsigned int i = 0; i < phnum; i++) {
-               if (!gelf_getphdr(elf, i, &phdr)) {
+               if (!gelf_getphdr(lfile->elf, i, &phdr)) {
                        ERROR(lfile->ctx, "Could not parse program header: %s\n", elf_errmsg(-1));
                        return -ENOTSUP;
                }
@@ -644,12 +594,8 @@ static int __pakfire_linter_file_check_execstack(
        return 0;
 }
 
-static int pakfire_linter_file_check_execstack(struct pakfire_linter_file* lfile) {
-       return pakfire_linter_file_elf(lfile, __pakfire_linter_file_check_execstack, NULL);
-}
-
 static int __pakfire_linter_file_has_bind_now(struct pakfire_linter_file* lfile,
-               Elf* elf, const GElf_Shdr* shdr, const GElf_Dyn* dyn, void* data) {
+               const GElf_Shdr* shdr, const GElf_Dyn* dyn, void* data) {
        int* has_bind_now = (int*)data;
 
        switch (dyn->d_tag) {
@@ -674,14 +620,13 @@ static int __pakfire_linter_file_has_bind_now(struct pakfire_linter_file* lfile,
        return 0;
 }
 
-static int __pakfire_linter_file_check_relro(
-               struct pakfire_linter_file* lfile, Elf* elf, void* data) {
+static int pakfire_linter_file_check_relro(struct pakfire_linter_file* lfile) {
        int has_bind_now = 0;
        GElf_Phdr phdr;
        int r;
 
        // Check if we have BIND_NOW
-       r = pakfire_linter_file_elf_dyn_walk(lfile, elf,
+       r = pakfire_linter_file_elf_dyn_walk(lfile,
                __pakfire_linter_file_has_bind_now, &has_bind_now);
        if (r)
                return r;
@@ -692,7 +637,7 @@ static int __pakfire_linter_file_check_relro(
 
        // Walk through all program headers
        for (unsigned int i = 0;; i++) {
-               if (!gelf_getphdr(elf, i, &phdr))
+               if (!gelf_getphdr(lfile->elf, i, &phdr))
                        break;
 
                switch (phdr.p_type) {
@@ -711,15 +656,11 @@ static int __pakfire_linter_file_check_relro(
        return pakfire_linter_file_error(lfile, "Is not fully RELRO");
 }
 
-static int pakfire_linter_file_check_relro(struct pakfire_linter_file* lfile) {
-       return pakfire_linter_file_elf(lfile, __pakfire_linter_file_check_relro, NULL);
-}
-
 /*
        RPATH/RUNPATH
 */
 static int __pakfire_linter_file_process_runpath(struct pakfire_linter_file* lfile,
-               Elf* elf, const GElf_Shdr* shdr, const GElf_Dyn* dyn, void* data) {
+               const GElf_Shdr* shdr, const GElf_Dyn* dyn, void* data) {
        const char* value = NULL;
        const char* runpath = NULL;
        char buffer[PATH_MAX];
@@ -730,7 +671,7 @@ static int __pakfire_linter_file_process_runpath(struct pakfire_linter_file* lfi
                case DT_RUNPATH:
                case DT_RPATH:
                        // Fetch the value
-                       value = elf_strptr(elf, shdr->sh_link, dyn->d_un.d_val);
+                       value = elf_strptr(lfile->elf, shdr->sh_link, dyn->d_un.d_val);
                        if (!value)
                                return 1;
 
@@ -780,14 +721,9 @@ RUNPATH_PERMITTED:
        return 0;
 }
 
-static int __pakfire_linter_file_check_runpath(
-               struct pakfire_linter_file* lfile, Elf* elf, void* data) {
-       return pakfire_linter_file_elf_dyn_walk(
-               lfile, elf, __pakfire_linter_file_process_runpath, data);
-}
-
 static int pakfire_linter_file_check_runpath(struct pakfire_linter_file* lfile) {
-       return pakfire_linter_file_elf(lfile, __pakfire_linter_file_check_runpath, NULL);
+       return pakfire_linter_file_elf_dyn_walk(
+               lfile, __pakfire_linter_file_process_runpath, NULL);
 }
 
 static uint32_t read_4_bytes(const int endianess, const char* data) {
@@ -850,8 +786,7 @@ static int __pakfire_linter_file_check_cf_protection_x86(struct pakfire_linter_f
        return 0;
 }
 
-static int pakfire_linter_file_check_cf_protection_callback(
-               struct pakfire_linter_file* lfile, Elf* elf,
+static int pakfire_linter_file_check_cf_protection_callback(struct pakfire_linter_file* lfile,
                const Elf_Scn* section, const GElf_Shdr* shdr, Elf_Data* data) {
        GElf_Ehdr ehdr = {};
        GElf_Nhdr nhdr = {};
@@ -861,7 +796,7 @@ static int pakfire_linter_file_check_cf_protection_callback(
        int r;
 
        // Fetch the ELF header
-       if (!gelf_getehdr(elf, &ehdr)) {
+       if (!gelf_getehdr(lfile->elf, &ehdr)) {
                ERROR(lfile->ctx, "Could not fetch the ELF header for %s: %m\n", lfile->path);
                return -errno;
        }
@@ -948,34 +883,15 @@ static int pakfire_linter_file_check_cf_protection_callback(
        return 0;
 }
 
-static int __pakfire_linter_file_check_cf_protection(
-               struct pakfire_linter_file* lfile, Elf* elf, void* data) {
-       return pakfire_linter_file_for_elf_section(
-               lfile, elf, SHT_NOTE, pakfire_linter_file_check_cf_protection_callback);
-}
-
 static int pakfire_linter_file_check_cf_protection(struct pakfire_linter_file* lfile) {
-       return pakfire_linter_file_elf(lfile, __pakfire_linter_file_check_cf_protection, NULL);
+       return pakfire_linter_file_for_elf_section(
+               lfile, SHT_NOTE, pakfire_linter_file_check_cf_protection_callback);
 }
 
-static int __pakfire_linter_file_is_stripped(
-               struct pakfire_linter_file* lfile, Elf* elf, void* data) {
+static int pakfire_linter_file_is_stripped(struct pakfire_linter_file* lfile) {
        Elf_Scn* symtab = NULL;
        int r;
 
-       // Fetch the symbol table
-       r = pakfire_linter_file_get_elf_section(lfile, elf, SHT_SYMTAB, &symtab, NULL, NULL);
-       if (r < 0)
-               return r;
-
-       // If we have found the symbol table we are not stripped
-       else if (r == 0)
-               return pakfire_linter_file_error(lfile, "Not Stripped");
-
-       return 0;
-}
-
-static int pakfire_linter_file_is_stripped(struct pakfire_linter_file* lfile) {
        switch (pakfire_linter_file_get_elf_type(lfile)) {
                // Do not check Relocatable Objects
                case ET_REL:
@@ -986,16 +902,24 @@ static int pakfire_linter_file_is_stripped(struct pakfire_linter_file* lfile) {
                        break;
        }
 
-       return pakfire_linter_file_elf(lfile, __pakfire_linter_file_is_stripped, NULL);
+       // Fetch the symbol table
+       r = pakfire_linter_file_get_elf_section(lfile, SHT_SYMTAB, &symtab, NULL, NULL);
+       if (r < 0)
+               return r;
+
+       // If we have found the symbol table we are not stripped
+       else if (r == 0)
+               return pakfire_linter_file_error(lfile, "Not Stripped");
+
+       return 0;
 }
 
-static int __pakfire_linter_file_has_debuglink(
-               struct pakfire_linter_file* lfile, Elf* elf, void* data) {
+static int pakfire_linter_file_has_debuglink(struct pakfire_linter_file* lfile) {
        const char* name = NULL;
        GElf_Word crc32;
 
        // Fetch the debug link
-       name = dwelf_elf_gnu_debuglink(elf, &crc32);
+       name = dwelf_elf_gnu_debuglink(lfile->elf, &crc32);
        if (!name)
                return pakfire_linter_file_error(lfile, "Missing Debug Link");
 
@@ -1004,17 +928,12 @@ static int __pakfire_linter_file_has_debuglink(
        return 0;
 }
 
-static int pakfire_linter_file_has_debuglink(struct pakfire_linter_file* lfile) {
-       return pakfire_linter_file_elf(lfile, __pakfire_linter_file_has_debuglink, NULL);
-}
-
-static int __pakfire_linter_file_has_buildid(
-               struct pakfire_linter_file* lfile, Elf* elf, void* data) {
+static int pakfire_linter_file_has_buildid(struct pakfire_linter_file* lfile) {
        const void* buildid = NULL;
        ssize_t length = 0;
 
        // Fetch the build ID
-       length = dwelf_elf_gnu_build_id(elf, &buildid);
+       length = dwelf_elf_gnu_build_id(lfile->elf, &buildid);
        if (length < 0)
                return length;
 
@@ -1025,10 +944,6 @@ static int __pakfire_linter_file_has_buildid(
        return 0;
 }
 
-static int pakfire_linter_file_has_buildid(struct pakfire_linter_file* lfile) {
-       return pakfire_linter_file_elf(lfile, __pakfire_linter_file_has_buildid, NULL);
-}
-
 int pakfire_linter_file_lint(struct pakfire_linter_file* lfile) {
        int r = 0;
 
index d0deeec6f2146d8b7a1c8fc58a591624e244d109..5bc14ac4b4d35407966d4c8c9dedd8fc2daa648f 100644 (file)
@@ -371,8 +371,10 @@ static int pakfire_linter_payload(
 
        // Lint the file
        r = pakfire_linter_file_lint(lfile);
-       if (r < 0)
+       if (r < 0) {
+               ERROR(linter->ctx, "Could not lint %s: %s\n", path, strerror(-r));
                goto ERROR;
+       }
 
 ERROR:
        if (lfile)