On Tue, May 18, 2021 at 2:21 PM Joel Sherrill <j...@rtems.org> wrote: > > > > On Tue, May 18, 2021 at 3:03 PM Ryan Long <ryan.l...@oarcorp.com> wrote: >> >> Improved tests for utime() and utimes() and update license. >> >> Close #4399 >> --- >> testsuites/psxtests/psx13/main.c | 5 +- >> testsuites/psxtests/psx13/test.c | 499 >> +++++++++++++++++++++++++++++++++++++-- >> 2 files changed, 481 insertions(+), 23 deletions(-) >> >> diff --git a/testsuites/psxtests/psx13/main.c >> b/testsuites/psxtests/psx13/main.c >> index f9e7907..7a560f9 100644 >> --- a/testsuites/psxtests/psx13/main.c >> +++ b/testsuites/psxtests/psx13/main.c >> @@ -1,5 +1,3 @@ >> -/* SPDX-License-Identifier: BSD-2-Clause */ >> - > > > Agree with Gedare. This shouldn't have been removed. > > There were some dates changed in the copyrights. > > Check all of the issues against all of the patches. > >> >> /** >> * @file >> * >> @@ -8,8 +6,7 @@ >> */ >> >> /* >> - * COPYRIGHT (c) 1989-2009, 2021. >> - * On-Line Applications Research Corporation (OAR). >> + * COPYRIGHT (C) 1989, 2021 On-Line Applications Research Corporation (OAR). > > > This changes the years which is wrong. >
Actually, we only document the first and last year now. >> >> * >> * Redistribution and use in source and binary forms, with or without >> * modification, are permitted provided that the following conditions >> diff --git a/testsuites/psxtests/psx13/test.c >> b/testsuites/psxtests/psx13/test.c >> index e98b03a..0754dbc 100644 >> --- a/testsuites/psxtests/psx13/test.c >> +++ b/testsuites/psxtests/psx13/test.c >> @@ -1,4 +1,4 @@ >> -/* SPDX-License-Identifier: BSD-2-Clause */ >> +/* SPDX-License-Identifier: BSD-2-Clause */ >> >> /** >> * @file >> @@ -17,12 +17,13 @@ >> * - umask() >> * - utime() >> * - utimes() >> + * - utimensat() >> + * - futimens() >> * - sync() >> */ >> >> /* >> - * COPYRIGHT (c) 1989-2009, 2021. >> - * On-Line Applications Research Corporation (OAR). >> + * COPYRIGHT (C) 1989, 2021 On-Line Applications Research Corporation (OAR). > > > Again a year change. >> >> * >> * Redistribution and use in source and binary forms, with or without >> * modification, are permitted provided that the following conditions >> @@ -52,6 +53,8 @@ >> >> #include <rtems.h> >> #include <rtems/libio.h> >> +#include <rtems/score/timespec.h> >> +#include <rtems/score/todimpl.h> >> #include <sys/time.h> >> #include <fcntl.h> >> #include <unistd.h> >> @@ -210,8 +213,7 @@ static void Dup2Test( void ) >> } >> >> /** >> - * @brief Exercises fdatasync(). Does NOT test the functionality of the >> - * underlying fdatasync entry in the IMFS op table. >> + * @brief Exercises fdatasync(). >> */ >> static void FDataSyncTest( void ) >> { >> @@ -259,60 +261,517 @@ static void UMaskTest( void ) >> } >> >> /** >> - * @brief Exercises utime(). Does not test the functionality of the >> - * underlying utime entry in the IMFS op table. >> + * @brief Exercises utime(). >> */ >> static void UTimeTest( void ) >> { >> int rv; >> struct utimbuf time; >> + struct timespec current_time; >> struct stat fstat; >> >> - /* First, an invalid filename. */ >> + /* ENOENT test case */ >> + >> + /* Case: Pass an invalid filename. */ >> rv = utime( "!This is an =invalid p@thname!!! :)", NULL ); >> rtems_test_assert( rv == -1 ); >> rtems_test_assert( errno == ENOENT ); >> >> - /* Now, the success test. */ >> + /* EACCES test case */ >> + >> + /* Case: Change user ID to someone besides root */ >> + rv = seteuid( 1 ); >> + rtems_test_assert( rv == 0 ); >> + >> + rv = utime( "testfile1.tst", NULL ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == EACCES ); >> + >> + rv = seteuid( 0 ); >> + rtems_test_assert( rv == 0 ); >> + >> + /* EINVAL test cases */ >> + >> + /* Case: Invalid access time */ >> + time.actime = -1; >> + time.modtime = 54321; >> + >> + rv = utime( "testfile1.tst", &time ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == EINVAL ); >> + >> + /* Case: Invalid modified time */ >> + time.actime = 12345; >> + time.modtime = -1; >> + >> + rv = utime( "testfile1.tst", &time ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == EINVAL ); >> + >> + /* Successful test cases */ >> + >> + /* Case: Test without times argument */ >> + clock_gettime( CLOCK_REALTIME, ¤t_time ); >> + >> + rv = utime( "testfile1.tst", NULL ); >> + rtems_test_assert( rv == 0 ); >> + >> + rv = stat( "testfile1.tst", &fstat ); >> + rtems_test_assert( rv == 0 ); >> + rtems_test_assert( current_time.tv_sec <= fstat.st_atim.tv_sec ); >> + rtems_test_assert( current_time.tv_sec <= fstat.st_mtim.tv_sec ); >> + >> + /* Case: time is filled with valid values */ >> time.actime = 12345; >> time.modtime = 54321; >> >> rv = utime( "testfile1.tst", &time ); >> rtems_test_assert( rv == 0 ); >> >> - /* But, did it set the time? */ >> + /* Check that it actually changed the time */ >> rv = stat( "testfile1.tst", &fstat ); >> rtems_test_assert( rv == 0 ); >> rtems_test_assert( fstat.st_atime == 12345 ); >> rtems_test_assert( fstat.st_mtime == 54321 ); >> - >> - rv = utime( "testfile1.tst", NULL ); >> - rtems_test_assert( rv == 0 ); >> } >> >> /** >> - * @brief Exercises utimes(). Does NOT test the functionality of the >> - * underlying utime entry in the IMFS op table. >> + * @brief Exercises utimes(). >> */ >> static void UTimesTest( void ) >> { >> int rv; >> struct timeval time[2]; >> + struct timespec current_time; >> struct stat fstat; >> >> - /* First, an invalid filename. */ >> + /* ENOENT test case */ >> + >> + /* Case: First, an invalid filename. */ >> rv = utimes( "!This is an =invalid p@thname!!! : )", NULL); >> rtems_test_assert( rv == -1 ); >> rtems_test_assert( errno == ENOENT ); >> >> - /* Now, the success test. */ >> + /* EACCES test case */ >> + >> + /* Change the user ID of the process to someone besides root */ >> + rv = seteuid( 1 ); >> + rtems_test_assert( rv == 0 ); >> + >> + rv = utimes( "testfile1.tst", NULL ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == EACCES ); >> + >> + rv = seteuid( 0 ); >> + rtems_test_assert( rv == 0 ); >> + >> + /* EINVAL test cases */ >> + >> + /* Case: Negative access time tv_sec value */ >> + time[0].tv_sec = -1; >> + time[0].tv_usec = 12345; >> + time[1].tv_sec = 54321; >> + time[1].tv_usec = 54321; >> + >> + rv = utimes( "testfile1.tst", time ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == EINVAL ); >> + >> + /* Case: Negative modified time second value */ >> time[0].tv_sec = 12345; >> + time[1].tv_sec = -1; >> + >> + rv = utimes( "testfile1.tst", time ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == EINVAL ); >> + >> + /* Case: Negative access time microsecond value */ >> time[1].tv_sec = 54321; >> + time[0].tv_usec = -1; >> + >> + rv = utimes( "testfile1.tst", time ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == EINVAL ); >> + >> + /* Case: Negative modified time microsecond value */ >> + time[0].tv_usec = 12345; >> + time[1].tv_usec = -1; >> + >> + rv = utimes( "testfile1.tst", time ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == EINVAL ); >> + >> + /* Case: Access time microsecond value too large */ >> + time[0].tv_usec = 1000000; >> + time[1].tv_usec = 54321; >> + >> + rv = utimes( "testfile1.tst", time ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == EINVAL ); >> + >> + /* Case: Modified time microsecond value too large */ >> + time[1].tv_usec = 1000000; >> + time[0].tv_usec = 12345; >> + >> + rv = utimes( "testfile1.tst", time ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == EINVAL ); >> + >> + /* Successful test cases */ >> + >> + /* Case: Test without times argument */ >> + clock_gettime( CLOCK_REALTIME, ¤t_time ); >> + >> + rv = utimes( "testfile1.tst", NULL ); >> + rtems_test_assert( rv == 0 ); >> + >> + rv = stat( "testfile1.tst", &fstat ); >> + rtems_test_assert( rv == 0 ); >> + rtems_test_assert( current_time.tv_sec <= fstat.st_atim.tv_sec ); >> + rtems_test_assert( current_time.tv_sec <= fstat.st_mtim.tv_sec ); >> + >> + /* Case: time is filled with valid values */ >> + time[0].tv_sec = 12345; >> + time[0].tv_usec = 12345; >> + time[1].tv_sec = 54321; >> + time[1].tv_usec = 54321; >> + >> + rv = utimes( "testfile1.tst", time ); >> + rtems_test_assert( rv == 0 ); >> + >> + /* Check that it actually changed the time */ >> + rv = stat( "testfile1.tst", &fstat ); >> + rtems_test_assert( rv == 0 ); >> + rtems_test_assert( fstat.st_atime == 12345 ); >> + rtems_test_assert( fstat.st_mtime == 54321 ); >> +} >> + >> +/** >> + * @brief Exercises utimensat(). >> + */ >> +static void UTimensatTest( void ) >> +{ >> + int rv; >> + struct timespec time[2]; >> + struct timespec current_time; >> + struct stat fstat; >> + >> + /* ENOSYS test cases */ >> + >> + /* Case: Pass an unsupported file descriptor */ >> + rv = utimensat( >> + 0, >> + "!This is an =invalid p@thname!!! : )", >> + NULL, >> + 0 >> + ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == ENOSYS ); >> + >> + /* Case: Pass unsupported flag */ >> + rv = utimensat( >> + AT_FDCWD, >> + "!This is an =invalid p@thname!!! : )", >> + NULL, >> + 1 >> + ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == ENOSYS ); >> + >> + /* ENOENT test case */ >> + >> + /* Use an invalid filename. */ >> + rv = utimensat( >> + AT_FDCWD, >> + "!This is an =invalid p@thname!!! : )", >> + NULL, >> + 0 >> + ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == ENOENT ); >> + >> + rv = stat( "testfile1.tst", &fstat ); >> + rtems_test_assert( rv == 0 ); >> + >> + /* EACCES test Cases */ >> + >> + /* Case: When times is NULL and the user has insufficient privileges */ >> + >> + /* Change the user ID of the process to someone besides root */ >> + rv = seteuid( 1 ); >> + rtems_test_assert( rv == 0 ); >> + >> + rv = utimensat( AT_FDCWD, "testfile1.tst", NULL, 0 ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == EACCES ); >> + >> + rv = seteuid( 0 ); >> + rtems_test_assert( rv == 0 ); >> + >> + /* Case: File is read-only and time's tv_nsec members are UTIME_NOW */ >> + >> + /* Change file to be read-only */ >> + rv = chmod( "testfile1.tst", 06444 ); >> + rtems_test_assert( rv == 0 ); >> + >> + _Timespec_Set( &time[0], 0, UTIME_NOW ); >> + _Timespec_Set( &time[1], 0, UTIME_NOW ); >> + >> + rv = utimensat( AT_FDCWD, "testfile1.tst", time, 0 ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == EACCES ); >> + >> + rv = chmod( "testfile1.tst", fstat.st_mode ); >> + rtems_test_assert( rv == 0 ); >> + >> + /* EINVAL test cases */ >> + >> + /* Case: Negative access time second value */ >> + _Timespec_Set( &time[0], -12345, 12345 ); >> + _Timespec_Set( &time[1], 54321, 54321 ); >> + >> + rv = utimensat( AT_FDCWD, "testfile1.tst", time, 0 ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == EINVAL ); >> + >> + /* Case: Negative modified time second value */ >> + _Timespec_Set( &time[0], 12345, 12345 ); >> + _Timespec_Set( &time[1], -54321, 54321 ); >> + >> + rv = utimensat( AT_FDCWD, "testfile1.tst", time, 0 ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == EINVAL ); >> + >> + /* Case: Negative access time nanosecond value */ >> + _Timespec_Set( &time[0], 12345, -12345 ); >> + _Timespec_Set( &time[1], 54321, 54321 ); >> + >> + rv = utimensat( AT_FDCWD, "testfile1.tst", time, 0 ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == EINVAL ); >> + >> + /* Case: Negative modified time nanosecond value */ >> + _Timespec_Set( &time[0], 12345, 12345 ); >> + _Timespec_Set( &time[1], 54321, -54321 ); >> + >> + rv = utimensat( AT_FDCWD, "testfile1.tst", time, 0 ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == EINVAL ); >> + >> + /* Case: Access time nanosecond value too large */ >> + _Timespec_Set( &time[0], 12345, TOD_NANOSECONDS_PER_SECOND ); >> + _Timespec_Set( &time[1], 54321, 54321 ); >> + >> + rv = utimensat( AT_FDCWD, "testfile1.tst", time, 0 ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == EINVAL ); >> + >> + /* Case: Modified time nanosecond value too large */ >> + _Timespec_Set( &time[0], 12345, 12345 ); >> + _Timespec_Set( &time[1], 54321, TOD_NANOSECONDS_PER_SECOND ); >> + >> + rv = utimensat( AT_FDCWD, "testfile1.tst", time, 0 ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == EINVAL ); >> + >> + /* Successful test cases */ >> + >> + /* Case: Test without times argument */ >> + clock_gettime( CLOCK_REALTIME, ¤t_time ); >> + >> + rv = utimensat( AT_FDCWD, "testfile1.tst", NULL, 0 ); >> + rtems_test_assert( rv == 0 ); >> + >> + rv = stat( "testfile1.tst", &fstat ); >> + rtems_test_assert( rv == 0 ); >> + rtems_test_assert( current_time.tv_sec <= fstat.st_atim.tv_sec ); >> + rtems_test_assert( current_time.tv_sec <= fstat.st_mtim.tv_sec ); >> + >> + /* Case: Running with access time nanosecond field equal to UTIME_NOW */ >> + _Timespec_Set( &time[0], 12345, UTIME_NOW ); >> + _Timespec_Set( &time[1], 54321, 54321 ); >> + >> + rv = utimensat( AT_FDCWD, "testfile1.tst", time, 0 ); >> + rtems_test_assert( rv == 0 ); >> + >> + rv = stat( "testfile1.tst", &fstat ); >> + rtems_test_assert( rv == 0 ); >> + rtems_test_assert( current_time.tv_sec <= fstat.st_atim.tv_sec ); >> + rtems_test_assert( fstat.st_mtime == 54321 ); >> + >> + /* Case: Running with modified time nanosecond field equal to UTIME_NOW */ >> + _Timespec_Set( &time[0], 12345, 12345 ); >> + _Timespec_Set( &time[1], 54321, UTIME_NOW ); >> + >> + rv = utimensat( AT_FDCWD, "testfile1.tst", time, 0 ); >> + rtems_test_assert( rv == 0 ); >> + >> + rv = stat( "testfile1.tst", &fstat ); >> + rtems_test_assert( rv == 0 ); >> + rtems_test_assert( fstat.st_atime == 12345 ); >> + rtems_test_assert( current_time.tv_sec <= fstat.st_mtim.tv_sec ); >> + >> + /* Case: Normal run */ >> + _Timespec_Set( &time[0], 12345, 12345 ); >> + _Timespec_Set( &time[1], 54321, 54321 ); >> + >> + rv = utimensat( AT_FDCWD, "testfile1.tst", time, 0 ); >> + rtems_test_assert( rv == 0 ); >> + >> + /* Check that it actually changed the time */ >> + rv = stat( "testfile1.tst", &fstat ); >> + rtems_test_assert( rv == 0 ); >> + rtems_test_assert( fstat.st_atime == 12345 ); >> + rtems_test_assert( fstat.st_mtime == 54321 ); >> +} >> + >> +/** >> + * @brief Exercises futimens(). >> + */ >> +static void FutimensTest( void ) >> +{ >> + int rv; >> + int fd; >> + struct timespec time[2]; >> + struct timespec current_time; >> + struct stat fstat; >> + >> + /* EBADF test case */ >> + >> + /* Case: Pass an invalid file descriptor */ >> + rv = futimens( -1, time ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == EBADF ); >> + >> + fd = open( "testfile1.tst", O_RDWR ); >> + rtems_test_assert( fd != -1 ); >> + >> + /* EACCES test cases */ >> + >> + /* Case: When times is NULL and the user has insufficient privileges */ >> + >> + /* Change the user ID of the process to someone besides root */ >> + rv = seteuid( 1 ); >> + rtems_test_assert( rv == 0 ); >> + >> + rv = futimens( fd, NULL ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == EACCES ); >> + >> + rv = seteuid( 0 ); >> + rtems_test_assert( rv == 0 ); >> + >> + /* Case: File is read-only and time's tv_nsec members are UTIME_NOW */ >> + >> + /* Change file to be read-only */ >> + rv = chmod( "testfile1.tst", 06444 ); >> + rtems_test_assert( rv == 0 ); >> + >> + _Timespec_Set( &time[0], 0, UTIME_NOW ); >> + _Timespec_Set( &time[1], 0, UTIME_NOW ); >> + >> + rv = futimens( fd, time ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == EACCES ); >> + >> + rv = chmod( "testfile1.tst", fstat.st_mode ); >> + rtems_test_assert( rv == 0 ); >> + >> + /* EINVAL test cases */ >> + >> + /* Case: Negative access time second value */ >> + _Timespec_Set( &time[0], -12345, 12345 ); >> + _Timespec_Set( &time[1], 54321, 54321 ); >> + >> + rv = futimens( fd, time ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == EINVAL ); >> + >> + /* Case: Negative modified time second value */ >> + _Timespec_Set( &time[0], 12345, 12345 ); >> + _Timespec_Set( &time[1], -54321, 54321 ); >> + >> + rv = futimens( fd, time ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == EINVAL ); >> + >> + /* Case: Negative access time nanosecond value */ >> + _Timespec_Set( &time[0], 12345, -12345 ); >> + _Timespec_Set( &time[1], 54321, 54321 ); >> + >> + rv = futimens( fd, time ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == EINVAL ); >> + >> + /* Case: Negative modified time nanosecond value */ >> + _Timespec_Set( &time[0], 12345, 12345 ); >> + _Timespec_Set( &time[1], 54321, -54321 ); >> + >> + rv = futimens( fd, time ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == EINVAL ); >> + >> + /* Case: Access time nanosecond value too large */ >> + _Timespec_Set( &time[0], 12345, TOD_NANOSECONDS_PER_SECOND ); >> + _Timespec_Set( &time[1], 54321, 54321 ); >> + >> + rv = futimens( fd, time ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == EINVAL ); >> + >> + /* Case: Modified time nanosecond value too large */ >> + _Timespec_Set( &time[0], 12345, 12345 ); >> + _Timespec_Set( &time[1], 54321, TOD_NANOSECONDS_PER_SECOND ); >> + >> + rv = futimens( fd, time ); >> + rtems_test_assert( rv == -1 ); >> + rtems_test_assert( errno == EINVAL ); >> + >> + /* Successful test cases */ >> + >> + /* Case: Test without times argument */ >> + clock_gettime( CLOCK_REALTIME, ¤t_time ); >> + >> + rv = futimens( fd, NULL ); >> + rtems_test_assert( rv == 0 ); >> + >> + rv = stat( "testfile1.tst", &fstat ); >> + rtems_test_assert( rv == 0 ); >> + rtems_test_assert( current_time.tv_sec <= fstat.st_atim.tv_sec ); >> + rtems_test_assert( current_time.tv_sec <= fstat.st_mtim.tv_sec ); >> + >> +/* Case: Running with access time nanosecond field equal to UTIME_NOW */ >> + _Timespec_Set( &time[0], 12345, UTIME_NOW ); >> + _Timespec_Set( &time[1], 54321, 54321 ); >> + >> + rv = futimens( fd, time ); >> + rtems_test_assert( rv == 0 ); >> + >> + rv = stat( "testfile1.tst", &fstat ); >> + rtems_test_assert( rv == 0 ); >> + rtems_test_assert( current_time.tv_sec <= fstat.st_atim.tv_sec ); >> + rtems_test_assert( fstat.st_mtime == 54321 ); >> + >> + /* Case: Running with modified time nanosecond field equal to UTIME_NOW */ >> + _Timespec_Set( &time[0], 12345, 12345 ); >> + _Timespec_Set( &time[1], 54321, UTIME_NOW ); >> + >> + rv = futimens( fd, time ); >> + rtems_test_assert( rv == 0 ); >> + >> + rv = stat( "testfile1.tst", &fstat ); >> + rtems_test_assert( rv == 0 ); >> + rtems_test_assert( fstat.st_atime == 12345 ); >> + rtems_test_assert( current_time.tv_sec <= fstat.st_mtim.tv_sec ); >> + >> + /* Case: Normal run */ >> + _Timespec_Set( &time[0], 12345, 12345 ); >> + _Timespec_Set( &time[1], 54321, 54321 ); >> >> - rv = utimes( "testfile1.tst", (struct timeval *)&time ); >> + rv = futimens( fd, time ); >> rtems_test_assert( rv == 0 ); >> >> - /* But, did it set the time? */ >> + /* Check that it actually changed the time */ >> rv = stat( "testfile1.tst", &fstat ); >> rtems_test_assert( rv == 0 ); >> rtems_test_assert( fstat.st_atime == 12345 ); >> @@ -443,6 +902,8 @@ int test_main( void ) >> UMaskTest(); >> UTimeTest(); >> UTimesTest(); >> + UTimensatTest(); >> + FutimensTest(); >> FSyncTest(); >> PathConfTest(); >> FPathConfTest(); >> -- >> 1.8.3.1 >> >> _______________________________________________ >> devel mailing list >> devel@rtems.org >> http://lists.rtems.org/mailman/listinfo/devel > > _______________________________________________ > devel mailing list > devel@rtems.org > http://lists.rtems.org/mailman/listinfo/devel _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel