1 #define _LARGEFILE64_SOURCE 1
11 #include <sys/ioctl.h>
15 static PyObject *selftest(PyObject *self, PyObject *args)
17 if (!PyArg_ParseTuple(args, ""))
20 return Py_BuildValue("i", !bupsplit_selftest());
24 static PyObject *blobbits(PyObject *self, PyObject *args)
26 if (!PyArg_ParseTuple(args, ""))
28 return Py_BuildValue("i", BUP_BLOBBITS);
32 static PyObject *splitbuf(PyObject *self, PyObject *args)
34 unsigned char *buf = NULL;
35 int len = 0, out = 0, bits = -1;
37 if (!PyArg_ParseTuple(args, "t#", &buf, &len))
39 out = bupsplit_find_ofs(buf, len, &bits);
40 return Py_BuildValue("ii", out, bits);
44 static PyObject *bitmatch(PyObject *self, PyObject *args)
46 unsigned char *buf1 = NULL, *buf2 = NULL;
47 int len1 = 0, len2 = 0;
50 if (!PyArg_ParseTuple(args, "t#t#", &buf1, &len1, &buf2, &len2))
54 for (byte = 0; byte < len1 && byte < len2; byte++)
56 int b1 = buf1[byte], b2 = buf2[byte];
59 for (bit = 0; bit < 8; bit++)
60 if ( (b1 & (0x80 >> bit)) != (b2 & (0x80 >> bit)) )
66 return Py_BuildValue("i", byte*8 + bit);
70 static PyObject *firstword(PyObject *self, PyObject *args)
72 unsigned char *buf = NULL;
76 if (!PyArg_ParseTuple(args, "t#", &buf, &len))
82 v = ntohl(*(uint32_t *)buf);
83 return Py_BuildValue("I", v);
87 static PyObject *extract_bits(PyObject *self, PyObject *args)
89 unsigned char *buf = NULL;
90 int len = 0, nbits = 0;
93 if (!PyArg_ParseTuple(args, "t#i", &buf, &len, &nbits))
99 mask = (1<<nbits) - 1;
100 v = ntohl(*(uint32_t *)buf);
101 v = (v >> (32-nbits)) & mask;
102 return Py_BuildValue("I", v);
106 // I would have made this a lower-level function that just fills in a buffer
107 // with random values, and then written those values from python. But that's
108 // about 20% slower in my tests, and since we typically generate random
109 // numbers for benchmarking other parts of bup, any slowness in generating
110 // random bytes will make our benchmarks inaccurate. Plus nobody wants
111 // pseudorandom bytes much except for this anyway.
112 static PyObject *write_random(PyObject *self, PyObject *args)
114 uint32_t buf[1024/4];
115 int fd = -1, seed = 0;
117 long long len = 0, kbytes = 0, written = 0;
119 if (!PyArg_ParseTuple(args, "iLi", &fd, &len, &seed))
124 for (kbytes = 0; kbytes < len/1024; kbytes++)
127 for (i = 0; i < sizeof(buf)/sizeof(buf[0]); i++)
129 ret = write(fd, buf, sizeof(buf));
133 if (ret < (int)sizeof(buf))
135 if (kbytes/1024 > 0 && !(kbytes%1024))
136 fprintf(stderr, "Random: %lld Mbytes\r", kbytes/1024);
139 // handle non-multiples of 1024
143 for (i = 0; i < sizeof(buf)/sizeof(buf[0]); i++)
145 ret = write(fd, buf, len % 1024);
152 fprintf(stderr, "Random: %lld Mbytes, done.\n", kbytes/1024);
153 return Py_BuildValue("L", written);
157 static PyObject *open_noatime(PyObject *self, PyObject *args)
159 char *filename = NULL;
160 int attrs, attrs_noatime, fd;
161 if (!PyArg_ParseTuple(args, "s", &filename))
168 attrs |= O_LARGEFILE;
170 attrs_noatime = attrs;
172 attrs_noatime |= O_NOATIME;
174 fd = open(filename, attrs_noatime);
175 if (fd < 0 && errno == EPERM)
177 // older Linux kernels would return EPERM if you used O_NOATIME
178 // and weren't the file's owner. This pointless restriction was
179 // relaxed eventually, but we have to handle it anyway.
180 // (VERY old kernels didn't recognized O_NOATIME, but they would
181 // just harmlessly ignore it, so this branch won't trigger)
182 fd = open(filename, attrs);
185 return PyErr_SetFromErrnoWithFilename(PyExc_IOError, filename);
186 return Py_BuildValue("i", fd);
190 static PyObject *fadvise_done(PyObject *self, PyObject *args)
194 if (!PyArg_ParseTuple(args, "iL", &fd, &ofs))
196 #ifdef POSIX_FADV_DONTNEED
197 posix_fadvise(fd, 0, ofs, POSIX_FADV_DONTNEED);
199 return Py_BuildValue("");
203 static PyObject *py_get_linux_file_attr(PyObject *self, PyObject *args)
210 if (!PyArg_ParseTuple(args, "s", &path))
213 fd = open(path, O_RDONLY | O_NONBLOCK | O_LARGEFILE | O_NOFOLLOW);
215 return PyErr_SetFromErrnoWithFilename(PyExc_IOError, path);
218 rc = ioctl(fd, FS_IOC_GETFLAGS, &attr);
222 return PyErr_SetFromErrnoWithFilename(PyExc_IOError, path);
226 return Py_BuildValue("k", attr);
230 static PyObject *py_set_linux_file_attr(PyObject *self, PyObject *args)
237 if (!PyArg_ParseTuple(args, "sk", &path, &attr))
240 fd = open(path, O_RDONLY | O_NONBLOCK | O_LARGEFILE | O_NOFOLLOW);
242 return PyErr_SetFromErrnoWithFilename(PyExc_IOError, path);
244 rc = ioctl(fd, FS_IOC_SETFLAGS, &attr);
248 return PyErr_SetFromErrnoWithFilename(PyExc_IOError, path);
256 static PyObject *py_lutimes(PyObject *self, PyObject *args)
260 double access, modification;
262 if (!PyArg_ParseTuple(args, "s(dd)", &filename, &access, &modification))
267 PyErr_SetString(PyExc_ValueError, "access time is NaN");
270 else if(isinf(access))
272 PyErr_SetString(PyExc_ValueError, "access time is infinite");
275 else if(isnan(modification))
277 PyErr_SetString(PyExc_ValueError, "modification time is NaN");
280 else if(isinf(modification))
282 PyErr_SetString(PyExc_ValueError, "modification time is infinite");
286 struct timeval tv[2];
288 double integral_part;
289 double fractional_part;
291 fractional_part = modf(access, &integral_part);
292 tv[0].tv_sec = integral_part;
293 tv[0].tv_usec = fractional_part * 1000000;
295 fractional_part = modf(modification, &integral_part);
296 tv[1].tv_sec = modification;
297 tv[1].tv_usec = fmod(modification, 1000000);
299 rc = lutimes(filename, tv);
301 return PyErr_SetFromErrnoWithFilename(PyExc_IOError, filename);
307 static PyMethodDef helper_methods[] = {
308 { "selftest", selftest, METH_VARARGS,
309 "Check that the rolling checksum rolls correctly (for unit tests)." },
310 { "blobbits", blobbits, METH_VARARGS,
311 "Return the number of bits in the rolling checksum." },
312 { "splitbuf", splitbuf, METH_VARARGS,
313 "Split a list of strings based on a rolling checksum." },
314 { "bitmatch", bitmatch, METH_VARARGS,
315 "Count the number of matching prefix bits between two strings." },
316 { "firstword", firstword, METH_VARARGS,
317 "Return an int corresponding to the first 32 bits of buf." },
318 { "extract_bits", extract_bits, METH_VARARGS,
319 "Take the first 'nbits' bits from 'buf' and return them as an int." },
320 { "write_random", write_random, METH_VARARGS,
321 "Write random bytes to the given file descriptor" },
322 { "open_noatime", open_noatime, METH_VARARGS,
323 "open() the given filename for read with O_NOATIME if possible" },
324 { "fadvise_done", fadvise_done, METH_VARARGS,
325 "Inform the kernel that we're finished with earlier parts of a file" },
326 { "get_linux_file_attr", py_get_linux_file_attr, METH_VARARGS,
327 "Return the Linux attributes for the given file." },
328 { "set_linux_file_attr", py_set_linux_file_attr, METH_VARARGS,
329 "Set the Linux attributes for the given file." },
330 { "lutimes", py_lutimes, METH_VARARGS,
331 "Set the access and modification times for the given file or symlink." },
332 { NULL, NULL, 0, NULL }, // sentinel
335 PyMODINIT_FUNC init_helpers(void)
337 Py_InitModule("_helpers", helper_methods);