summaryrefslogtreecommitdiffstats
path: root/lib/blkid/llseek.c
blob: 59298646fd3611725765104d3a0a4862ab6f63dc (plain)
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
146
147
/*
 * llseek.c -- stub calling the llseek system call
 *
 * Copyright (C) 1994, 1995, 1996, 1997 Theodore Ts'o.
 *
 * %Begin-Header%
 * This file may be redistributed under the terms of the
 * GNU Lesser General Public License.
 * %End-Header%
 */

#ifndef _LARGEFILE_SOURCE
#define _LARGEFILE_SOURCE
#endif
#ifndef _LARGEFILE64_SOURCE
#define _LARGEFILE64_SOURCE
#endif

#include "config.h"
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif

#if HAVE_ERRNO_H
#include <errno.h>
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef __MSDOS__
#include <io.h>
#endif

#include "blkidP.h"

#ifdef __linux__

#if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE)

#define my_llseek lseek64

#elif defined(HAVE_LLSEEK)
#include <sys/syscall.h>

#ifndef HAVE_LLSEEK_PROTOTYPE
extern long long llseek(int fd, long long offset, int origin);
#endif

#define my_llseek llseek

#else	/* ! HAVE_LLSEEK */

#if SIZEOF_LONG == SIZEOF_LONG_LONG

#define llseek lseek

#else /* SIZEOF_LONG != SIZEOF_LONG_LONG */

#include <linux/unistd.h>

#ifndef __NR__llseek
#define __NR__llseek            140
#endif

#ifndef __i386__
static int _llseek(unsigned int, unsigned long, unsigned long,
		   blkid_loff_t *, unsigned int);

static _syscall5(int, _llseek, unsigned int, fd, unsigned long, offset_high,
		 unsigned long, offset_low, blkid_loff_t *, result,
		 unsigned int, origin)
#endif

static blkid_loff_t my_llseek(int fd, blkid_loff_t offset, int origin)
{
	blkid_loff_t result;
	int retval;

#ifndef __i386__
	retval = _llseek(fd, ((unsigned long long) offset) >> 32,
			 ((unsigned long long)offset) & 0xffffffff,
			 &result, origin);
#else
	retval = syscall(__NR__llseek, fd, ((unsigned long long) offset) >> 32,
			 ((unsigned long long)offset) & 0xffffffff,
			 &result, origin);
#endif
	return (retval == -1 ? (blkid_loff_t) retval : result);
}

#endif	/* __alpha__ || __ia64__ */

#endif /* HAVE_LLSEEK */

blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int whence)
{
	blkid_loff_t result;
	static int do_compat = 0;

	if ((sizeof(off_t) >= sizeof(blkid_loff_t)) ||
	    (offset < ((blkid_loff_t) 1 << ((sizeof(off_t)*8) -1))))
		return lseek(fd, (off_t) offset, whence);

	if (do_compat) {
		errno = EOVERFLOW;
		return -1;
	}

	result = my_llseek(fd, offset, whence);
	if (result == -1 && errno == ENOSYS) {
		/*
		 * Just in case this code runs on top of an old kernel
		 * which does not support the llseek system call
		 */
		do_compat++;
		errno = EOVERFLOW;
	}
	return result;
}

#else /* !linux */

#ifndef EOVERFLOW
#ifdef EXT2_ET_INVALID_ARGUMENT
#define EOVERFLOW EXT2_ET_INVALID_ARGUMENT
#else
#define EOVERFLOW 112
#endif
#endif

blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int origin)
{
#if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE)
	return lseek64 (fd, offset, origin);
#else
	if ((sizeof(off_t) < sizeof(blkid_loff_t)) &&
	    (offset >= ((blkid_loff_t) 1 << ((sizeof(off_t)*8) - 1)))) {
		errno = EOVERFLOW;
		return -1;
	}
	return lseek(fd, (off_t) offset, origin);
#endif
}

#endif	/* linux */