about summary refs log tree commit diff
path: root/sysdeps/unix/sysv/linux/tst-opendir.c
blob: 216ecf123f59e4b153d1e81ed1ba7fbe815bc447 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/* Check multiple telldir and seekdir.
   Copyright (C) 2023 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <https://www.gnu.org/licenses/>.  */

#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <support/check.h>
#include <support/support.h>
#include <support/temp_file.h>
#include <support/xunistd.h>

/* Some filesystems returns an arbitrary value for d_off direnty entry (ext4
   for instance, where the value is an internal hash key).  The idea of create
   a large number of file is to try trigger a overflow d_off value in a entry
   to check if telldir/seekdir does work corretly in such case.  */
static const char *dirname;
/* The 2 extra files are '.' and '..'.  */
static const size_t nfiles = (1<<14) + 2;

static inline bool
in_ino_t_range (ino64_t v)
{
  ino_t s = v;
  return s == v;
}

static inline bool
in_off_t_range (off64_t v)
{
  off_t s = v;
  return s == v;
}

static void
do_prepare (int argc, char *argv[])
{
  dirname = support_create_temp_directory ("tst-opendir-nolfs-");

  for (size_t i = 0; i < nfiles - 2; i++)
    {
      int fd = create_temp_file_in_dir ("tempfile.", dirname, NULL);
      TEST_VERIFY_EXIT (fd > 0);
      close (fd);
    }
}
#define PREPARE do_prepare

static int
do_test (void)
{
  DIR *dirp = opendir (dirname);
  TEST_VERIFY_EXIT (dirp != NULL);

  long int *tdirp = xreallocarray (NULL, nfiles, sizeof (long int));
  struct dirent **ddirp = xreallocarray (NULL, nfiles,
					 sizeof (struct dirent *));

  /* For non-LFS, the entry is skipped if it can not be converted.  */
  int count = 0;
  for (; count < nfiles; count++)
    {
      struct dirent *dp = readdir (dirp);
      if (dp == NULL)
	break;
      tdirp[count] = telldir (dirp);
      ddirp[count] = xmalloc (dp->d_reclen);
      memcpy (ddirp[count], dp, dp->d_reclen);
    }

  closedir (dirp);

  /* Check against the getdents64 syscall.  */
  int fd = xopen (dirname, O_RDONLY | O_DIRECTORY, 0);
  int i = 0;
  while (true)
    {
      struct
      {
	char buffer[1024];
	struct dirent64 pad;
      } data;

      ssize_t ret = getdents64 (fd, &data.buffer, sizeof (data.buffer));
      if (ret < 0)
	FAIL_EXIT1 ("getdents64: %m");
      if (ret == 0)
	break;

      char *current = data.buffer;
      char *end = data.buffer + ret;
      while (current != end)
	{
	  struct dirent64 entry;
          memcpy (&entry, current, sizeof (entry));
          /* Truncate overlong strings.  */
          entry.d_name[sizeof (entry.d_name) - 1] = '\0';
          TEST_VERIFY (strlen (entry.d_name) < sizeof (entry.d_name) - 1);

	  if (in_ino_t_range (entry.d_ino))
	    {
	      TEST_COMPARE_STRING (entry.d_name, ddirp[i]->d_name);
	      TEST_COMPARE (entry.d_ino, ddirp[i]->d_ino);
	      TEST_COMPARE (entry.d_type, ddirp[i]->d_type);

	      /* Offset zero is reserved for the first entry.  */
	      TEST_VERIFY (entry.d_off != 0);

	      TEST_VERIFY_EXIT (entry.d_reclen <= end - current);
	      i++;
	    }

	  current += entry.d_reclen;
	}
    }

  TEST_COMPARE (count, i);

  free (tdirp);
  for (int i = 0; i < count; i++)
    free (ddirp[i]);
  free (ddirp);

  return 0;
}

#include <support/test-driver.c>