Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Free (deleting) allocated memory from the function readdir

I am using the C programming language in the Linux environment to read the files in a directory. I have include #include<dirent.h> in my code and am using the function readdir().

According to the Linux page online it says not to call free() on the resulting pointer to a dirent structure because it may be allocated on the stack.

Can you help me understand how that works? I don't understand why we would not have to delete the struct dirent. When is it deleted and who deletes it?

Here is the excerpt I am talking about:

On success, readdir() returns a pointer to a dirent structure. (This structure may be statically allocated; do not attempt to free(3) it.) If the end of the directory stream is reached, NULL is returned and errno is not changed. If an error occurs, NULL is returned and errno is set appropriately.

like image 412
Matthew Avatar asked Dec 31 '15 19:12

Matthew


People also ask

What is free() in c++?

The free() function in C++ deallocates a block of memory previously allocated using calloc, malloc or realloc functions, making it available for further allocations. The free() function does not change the value of the pointer, that is it still points to the same memory location.

Should I use free or delete c++?

In C++, the delete operator should only be used either for the pointers pointing to the memory allocated using new operator or for a NULL pointer, and free() should only be used either for the pointers pointing to the memory allocated using malloc() or for a NULL pointer. It is an operator. It is a library function.

What does Readdir return in C?

The readdir() function returns a pointer to a structure representing the directory entry at the current position in the directory stream specified by the argument dirp, and positions the directory stream at the next entry. It returns a null pointer upon reaching the end of the directory stream.

Do you need to free pointers in c++?

There is no need to free any pointer as you are not doing any dynamic memory allocation here. Save this answer.


2 Answers

man readdir literally says:

On success, readdir() returns a pointer to a dirent structure. (This structure may be statically allocated; do not attempt to free(3) it.)

(Code formatters added.)

That means the space for it is not allocated at runtime such as stack or free store memory but is static: it is in the executable itsself, comparable to string literals with the difference that writing to string literals is undefined behavior.

Imagine the implementation to be something like this:

struct dirent *readdir(DIR *dirp) {
    static struct dirent dir;

    /* Fill dir with appropriate values. */

    return &dir;
}

dir is statically allocated here. Returning its address isn't wrong because it exists throughout the whole runtime of the program.

Here is the actual source code of readdir on my glibc 2.22 implementation (the path is /sysdeps/posix/readdir.c):

DIRENT_TYPE *
__READDIR (DIR *dirp)
{
  DIRENT_TYPE *dp;
  int saved_errno = errno;

#if IS_IN (libc)
  __libc_lock_lock (dirp->lock);
#endif

  do
    {
      size_t reclen;

      if (dirp->offset >= dirp->size)
    {
      /* We've emptied out our buffer.  Refill it.  */

      size_t maxread;
      ssize_t bytes;

#ifndef _DIRENT_HAVE_D_RECLEN
      /* Fixed-size struct; must read one at a time (see below).  */
      maxread = sizeof *dp;
#else
      maxread = dirp->allocation;
#endif

      bytes = __GETDENTS (dirp->fd, dirp->data, maxread);
      if (bytes <= 0)
        {
          /* On some systems getdents fails with ENOENT when the
         open directory has been rmdir'd already.  POSIX.1
         requires that we treat this condition like normal EOF.  */
          if (bytes < 0 && errno == ENOENT)
        bytes = 0;

          /* Don't modifiy errno when reaching EOF.  */
          if (bytes == 0)
        __set_errno (saved_errno);
          dp = NULL;
          break;
        }
      dirp->size = (size_t) bytes;

      /* Reset the offset into the buffer.  */
      dirp->offset = 0;
    }

      dp = (DIRENT_TYPE *) &dirp->data[dirp->offset];

#ifdef _DIRENT_HAVE_D_RECLEN
      reclen = dp->d_reclen;
#else
      /* The only version of `struct dirent*' that lacks `d_reclen'
     is fixed-size.  */
      assert (sizeof dp->d_name > 1);
      reclen = sizeof *dp;
      /* The name is not terminated if it is the largest possible size.
     Clobber the following byte to ensure proper null termination.  We
     read jst one entry at a time above so we know that byte will not
     be used later.  */
      dp->d_name[sizeof dp->d_name] = '\0';
#endif

      dirp->offset += reclen;

#ifdef _DIRENT_HAVE_D_OFF
      dirp->filepos = dp->d_off;
#else
      dirp->filepos += reclen;
#endif

      /* Skip deleted files.  */
    } while (dp->d_ino == 0);

#if IS_IN (libc)
  __libc_lock_unlock (dirp->lock);
#endif

  return dp;
}

I don't know much about glibc but the line

dp = (DIRENT_TYPE *) &dirp->data[dirp->offset];

seems the most interesting to us. dirp->data is the static data here, as far as I can tell.


That is the reason as to why there is the reentrant alternative readdir_r and readdir is not reentrant.
Imagine two threads concurrently executing readdir. Both will attempt to fill dir, which is shared among all readdir invocations, simultaneously, resulting in unsequenced memory reads/writes.

like image 80
cadaniluk Avatar answered Oct 31 '22 15:10

cadaniluk


The man page you reference is cautioning that the struct dirent is statically allocated. Therefore free()is not necessary.

free() is designed exclusively for use with [m][c][re]alloc() functions, which all make requests for memory from the heap. (as opposed to the stack)

like image 4
ryyker Avatar answered Oct 31 '22 13:10

ryyker