2 * Copyright 2012 GRNET S.A. All rights reserved.
4 * Redistribution and use in source and binary forms, with or
5 * without modification, are permitted provided that the following
8 * 1. Redistributions of source code must retain the above
9 * copyright notice, this list of conditions and the following
11 * 2. Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following
13 * disclaimer in the documentation and/or other materials
14 * provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
29 * The views and conclusions contained in the software and
30 * documentation are those of the authors and should not be
31 * interpreted as representing official policies, either expressed
32 * or implied, of GRNET S.A.
36 * The Pithos File Blocker Peer (pfiled)
43 #include <sys/types.h>
53 #include <sys/sendfile.h>
55 #include <xtypes/xcache.h>
56 #include <openssl/sha.h>
57 #include <sys/resource.h>
59 #include <xseg/xseg.h>
60 #include <xseg/protocol.h>
64 #define FIO_STR_ID_LEN 3
65 #define LOCK_SUFFIX "_lock"
66 #define LOCK_SUFFIX_LEN 5
67 #define HASH_SUFFIX "_hash"
68 #define HASH_SUFFIX_LEN 5
69 #define MAX_PATH_SIZE 1024
70 #define MAX_FILENAME_SIZE (XSEG_MAX_TARGETLEN + LOCK_SUFFIX_LEN + MAX_UNIQUESTR_LEN + FIO_STR_ID_LEN)
71 #define MAX_PREFIX_LEN 10
72 #define MAX_UNIQUESTR_LEN 128
73 #define SNAP_SUFFIX "_snap"
74 #define SNAP_SUFFIX_LEN 5
79 #define min(_a, _b) (_a < _b ? _a : _b)
82 * Globals, holding command-line arguments
85 void custom_peer_usage(char *argv0)
87 fprintf(stderr, "General peer options:\n"
88 " Option | Default | \n"
89 " --------------------------------------------\n"
90 " --fdcache | 2 * nr_ops | Fd cache size\n"
91 " --archip | None | Archipelago directory\n"
92 " --prefix | None | Common prefix of objects that should be stripped\n"
93 " --uniquestr | None | Unique string for this instance\n"
98 /* fdcache_node flags */
99 #define READY (1 << 1)
101 /* fdcache node info */
102 struct fdcache_entry {
104 volatile unsigned int flags;
111 uint32_t uniquestr_len;
114 char vpath[MAX_PATH_SIZE + 1];
115 char prefix[MAX_PREFIX_LEN + 1];
116 char uniquestr[MAX_UNIQUESTR_LEN + 1];
121 * pfiled specific structure
122 * containing information on a pending I/O operation
127 char str_id[FIO_STR_ID_LEN];
130 struct pfiled * __get_pfiled(struct peerd *peer)
132 return (struct pfiled *) peer->priv;
135 struct fio * __get_fio(struct peer_req *pr)
137 return (struct fio*) pr->priv;
142 static void * cache_node_init(void *p, void *xh)
144 //struct peerd *peer = (struct peerd *)p;
145 //struct pfiled *pfiled = __get_pfiled(peer);
146 xcache_handler h = *(xcache_handler *)(xh);
147 struct fdcache_entry *fdentry = malloc(sizeof(struct fdcache_entry));
151 XSEGLOG2(&lc, D, "Initialing node h: %llu with %p",
152 (long long unsigned)h, fdentry);
160 static int cache_init(void *p, void *e)
162 struct fdcache_entry *fdentry = (struct fdcache_entry *)e;
164 if (fdentry->fd != -1) {
165 XSEGLOG2(&lc, E, "Found invalid fd %d", fdentry->fd);
172 static void cache_put(void *p, void *e)
174 struct fdcache_entry *fdentry = (struct fdcache_entry *)e;
176 XSEGLOG2(&lc, D, "Putting entry %p with fd %d", fdentry, fdentry->fd);
178 if (fdentry->fd != -1)
186 static void close_cache_entry(struct peerd *peer, struct peer_req *pr)
188 struct pfiled *pfiled = __get_pfiled(peer);
189 struct fio *fio = __get_fio(pr);
190 if (fio->h != NoEntry)
191 xcache_put(&pfiled->cache, fio->h);
194 static void pfiled_complete(struct peerd *peer, struct peer_req *pr)
196 close_cache_entry(peer, pr);
200 static void pfiled_fail(struct peerd *peer, struct peer_req *pr)
202 close_cache_entry(peer, pr);
206 static void handle_unknown(struct peerd *peer, struct peer_req *pr)
208 XSEGLOG2(&lc, W, "unknown request op");
209 pfiled_fail(peer, pr);
212 static void get_dirs(char buf[6], struct pfiled *pfiled, char *target, uint32_t targetlen)
214 unsigned char sha[SHA256_DIGEST_SIZE];
215 char hex[HEXLIFIED_SHA256_DIGEST_SIZE];
216 char *prefix = pfiled->prefix;
217 uint32_t prefixlen = pfiled->prefix_len;
219 if (strncmp(target, prefix, prefixlen)) {
220 strncpy(buf, target, 6);
224 SHA256((unsigned char *)target, targetlen, sha);
225 hexlify(sha, 3, hex);
226 strncpy(buf, hex, 6);
230 static int strnjoin(char *dest, int n, ...)
239 for (i = 0; i < n; i++) {
240 s = va_arg(ap, char *);
242 strncpy(dest + pos, s, l);
251 static int strjoin(char *dest, char *f, int f_len, char *s, int s_len)
256 strncpy(dest + pos, f, f_len);
258 strncpy(dest + pos, s, s_len);
262 return f_len + s_len;
265 static int create_path(char *buf, struct pfiled *pfiled, char *target,
266 uint32_t targetlen, int mkdirs)
271 char *path = pfiled->vpath;
272 uint32_t pathlen = pfiled->vpath_len;
274 get_dirs(dirs, pfiled, target, targetlen);
276 strncpy(buf, path, pathlen);
278 for (i = 0; i < 9; i+= 3) {
279 buf[pathlen + i] = dirs[i - (i/3)];
280 buf[pathlen + i +1] = dirs[i + 1 - (i/3)];
281 buf[pathlen + i + 2] = '/';
283 buf[pathlen + i + 3] = '\0';
285 if (stat(buf, &st) < 0)
286 if (mkdir(buf, 0750) < 0) {
295 strncpy(&buf[pathlen + 9], target, targetlen);
296 buf[pathlen + 9 + targetlen] = '\0';
302 static ssize_t persisting_read(int fd, void *data, size_t size, off_t offset)
304 ssize_t r = 0, sum = 0;
305 char error_str[1024];
306 XSEGLOG2(&lc, D, "fd: %d, size: %d, offset: %d", fd, size, offset);
309 XSEGLOG2(&lc, D, "read: %llu, (aligned)size: %llu", sum, size);
310 r = pread(fd, (char *)data + sum, size - sum, offset + sum);
312 XSEGLOG2(&lc, E, "fd: %d, Error: %s", fd, strerror_r(errno, error_str, 1023));
320 XSEGLOG2(&lc, D, "read: %llu, (aligned)size: %llu", sum, size);
322 if (sum == 0 && r < 0) {
325 XSEGLOG2(&lc, D, "Finished. Read %d, r = %d", sum, r);
330 static ssize_t persisting_write(int fd, void *data, size_t size, off_t offset)
332 ssize_t r = 0, sum = 0;
334 XSEGLOG2(&lc, D, "fd: %d, size: %d, offset: %d", fd, size, offset);
336 XSEGLOG2(&lc, D, "written: %llu, (aligned)size: %llu", sum, size);
337 r = pwrite(fd, (char *)data + sum, size - sum, offset + sum);
344 XSEGLOG2(&lc, D, "written: %llu, (aligned)size: %llu", sum, size);
346 if (sum == 0 && r < 0) {
349 XSEGLOG2(&lc, D, "Finished. Wrote %d, r = %d", sum, r);
354 static ssize_t aligned_read(int fd, void *data, ssize_t size, off_t offset, int alignment)
358 size_t misaligned_data, misaligned_size, misaligned_offset;
359 off_t aligned_offset=offset;
360 size_t aligned_size=size;
362 misaligned_data = (unsigned long)data % alignment;
363 misaligned_size = size % alignment;
364 misaligned_offset = offset % alignment;
365 XSEGLOG2(&lc, D, "misaligned_data: %u, misaligned_size: %u, misaligned_offset: %u", misaligned_data, misaligned_size, misaligned_offset);
366 if (misaligned_data || misaligned_size || misaligned_offset) {
367 aligned_offset = offset - misaligned_offset;
368 aligned_size = size + misaligned_offset;
370 misaligned_size = aligned_size % alignment;
371 aligned_size = aligned_size - misaligned_size + alignment;
372 r = posix_memalign(&tmp_data, alignment, aligned_size);
378 aligned_offset = offset;
382 XSEGLOG2(&lc, D, "aligned_data: %u, aligned_size: %u, aligned_offset: %u", tmp_data, aligned_size, aligned_offset);
383 r = persisting_read(fd, tmp_data, aligned_size, aligned_offset);
385 //FIXME if r < size ?
386 if (tmp_data != data) {
387 memcpy(data, tmp_data + misaligned_offset, size);
395 pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
397 int __fcntl_lock(int fd, off_t start, off_t len)
399 return pthread_mutex_lock(&m);
402 int __fcntl_unlock(int fd, off_t start, off_t len)
404 return pthread_mutex_unlock(&m);
407 static ssize_t aligned_write(int fd, void *data, size_t size, off_t offset, int alignment)
412 size_t misaligned_data, misaligned_size, misaligned_offset;
413 size_t aligned_size = size, aligned_offset = offset, read_size;
414 misaligned_data = (unsigned long)data % alignment;
415 misaligned_size = size % alignment;
416 misaligned_offset = offset % alignment;
417 if (misaligned_data || misaligned_size || misaligned_offset) {
418 //if somthing is misaligned then:
420 // First check if the offset was missaligned.
421 aligned_offset = offset - misaligned_offset;
423 // Then adjust the size with the misaligned offset and check if
424 // it remains misaligned.
425 aligned_size = size + misaligned_offset;
426 misaligned_size = aligned_size % alignment;
428 // in case there is no misaligned_size
430 aligned_size = aligned_size + alignment - misaligned_size;
432 // Allocate aligned memory
433 r = posix_memalign(&tmp_data, alignment, aligned_size);
438 XSEGLOG2(&lc, D, "fd: %d, misaligned_data: %u, misaligned_size: %u, misaligned_offset: %u", fd, misaligned_data, misaligned_size, misaligned_offset);
439 XSEGLOG2(&lc, D, "fd: %d, aligned_data: %u, aligned_size: %u, aligned_offset: %u", fd, tmp_data, aligned_size, aligned_offset);
440 XSEGLOG2(&lc, D, "fd: %d, locking from %u to %u", fd, aligned_offset, aligned_offset + aligned_size);
441 __fcntl_lock(fd, aligned_offset, aligned_size + alignment - misaligned_size);
444 if (misaligned_offset) {
445 XSEGLOG2(&lc, D, "fd: %d, size: %d, offset: %d", fd, size, offset);
446 /* read misaligned_offset */
447 read_size = alignment;
448 r = persisting_read(fd, tmp_data, alignment, aligned_offset);
452 } else if (r != read_size) {
453 memset(tmp_data + r, 0, read_size - r);
457 if (misaligned_size) {
458 read_size = alignment;
459 r = persisting_read(fd, tmp_data + aligned_size - alignment, alignment,
460 aligned_offset + aligned_size - alignment);
464 } else if (r != read_size) {
465 memset(tmp_data + aligned_size - alignment + r, 0, read_size - r);
468 memcpy(tmp_data + misaligned_offset, data, size);
471 aligned_offset = offset;
475 r = persisting_write(fd, tmp_data, aligned_size, aligned_offset);
478 XSEGLOG2(&lc, D, "fd: %d, unlocking from %u to %u", fd, aligned_offset, aligned_offset + aligned_size);
479 __fcntl_unlock(fd, aligned_offset, aligned_size + alignment - misaligned_size);
481 if (tmp_data != data) {
490 static ssize_t filed_write(int fd, void *data, size_t size, off_t offset, int direct)
493 return aligned_write(fd, data, size, offset, 512);
495 return persisting_write(fd, data, size, offset);
498 static ssize_t filed_read(int fd, void *data, size_t size, off_t offset, int direct)
501 return aligned_read(fd, data, size, offset, 512);
503 return persisting_read(fd, data, size, offset);
506 static ssize_t pfiled_read(struct pfiled *pfiled, int fd, void *data, size_t size, off_t offset)
508 return filed_read(fd, data, size, offset, pfiled->directio);
511 static ssize_t pfiled_write(struct pfiled *pfiled, int fd, void *data, size_t size, off_t offset)
513 return filed_write(fd, data, size, offset, pfiled->directio);
516 static ssize_t generic_io_path(char *path, void *data, size_t size, off_t offset, int write, int flags, mode_t mode)
521 fd = open(path, flags, mode);
525 XSEGLOG2(&lc, D, "Opened file %s as fd %d", path, fd);
528 r = filed_write(fd, data, size, offset, flags & O_DIRECT);
530 r = filed_read(fd, data, size, offset, flags & O_DIRECT);
538 static ssize_t read_path(char *path, void *data, size_t size, off_t offset, int direct)
540 int flags = O_RDONLY;
544 return generic_io_path(path, data, size, offset, 0, flags, 0);
547 static ssize_t pfiled_read_name(struct pfiled *pfiled, char *name, uint32_t namelen, void *data, size_t size, off_t offset)
549 char path[XSEG_MAX_TARGETLEN + MAX_PATH_SIZE + 1];
551 r = create_path(path, pfiled, name, namelen, 1);
553 XSEGLOG2(&lc, E, "Could not create path");
556 return read_path(path, data, size, offset, pfiled->directio);
559 static ssize_t write_path(char *path, void *data, size_t size, off_t offset, int direct, int extra_open_flags, mode_t mode)
561 int flags = O_RDWR | extra_open_flags;
564 return generic_io_path(path, data, size, offset, 1, flags, mode);
567 static ssize_t pfiled_write_name(struct pfiled *pfiled, char *name, uint32_t namelen, void *data, size_t size, off_t offset, int extra_open_flags, mode_t mode)
569 char path[XSEG_MAX_TARGETLEN + MAX_PATH_SIZE + 1];
571 r = create_path(path, pfiled, name, namelen, 1);
573 XSEGLOG2(&lc, E, "Could not create path");
576 return write_path(path, data, size, offset, pfiled->directio, extra_open_flags, mode);
579 static int is_target_valid_len(struct pfiled *pfiled, char *target,
580 uint32_t targetlen, int mode)
582 if (targetlen > XSEG_MAX_TARGETLEN) {
583 XSEGLOG2(&lc, E, "Invalid targetlen %u, max: %u",
584 targetlen, XSEG_MAX_TARGETLEN);
587 if (mode == WRITE || mode == READ) {
589 * if name starts with prefix
590 * assert targetlen >= prefix_len + 6
592 * assert targetlen >= 6
594 /* 6 chars are needed for the directory structrure */
595 if (!pfiled->prefix_len || strncmp(target, pfiled->prefix, pfiled->prefix_len)) {
597 XSEGLOG2(&lc, E, "Targetlen should be at least 6");
601 if (targetlen < pfiled->prefix_len + 6) {
602 XSEGLOG2(&lc, E, "Targetlen should be at least prefix "
603 "len(%u) + 6", pfiled->prefix_len);
608 XSEGLOG2(&lc, E, "Invalid mode");
616 static int is_target_valid(struct pfiled *pfiled, char *target, int mode)
618 return is_target_valid_len(pfiled, target, strlen(target), mode);
622 static int open_file_write(struct pfiled *pfiled, char *target, uint32_t targetlen)
625 char tmp[XSEG_MAX_TARGETLEN + MAX_PATH_SIZE + 1];
626 char error_str[1024];
628 r = create_path(tmp, pfiled, target, targetlen, 1);
630 XSEGLOG2(&lc, E, "Could not create path");
633 flags = O_RDWR|O_CREAT;
634 if (pfiled->directio)
636 XSEGLOG2(&lc, D, "Opening file %s with O_RDWR|O_CREAT", tmp);
637 fd = open(tmp, flags, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
639 XSEGLOG2(&lc, E, "Could not open file %s. Error: %s", tmp, strerror_r(errno, error_str, 1023));
645 static int open_file_read(struct pfiled *pfiled, char *target, uint32_t targetlen)
648 char tmp[XSEG_MAX_TARGETLEN + MAX_PATH_SIZE + 1];
649 char error_str[1024];
651 r = create_path(tmp, pfiled, target, targetlen, 0);
653 XSEGLOG2(&lc, E, "Could not create path");
656 XSEGLOG2(&lc, D, "Opening file %s with O_RDWR", tmp);
658 if (pfiled->directio)
660 fd = open(tmp, flags, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
662 XSEGLOG2(&lc, E, "Could not open file %s. Error: %s", tmp, strerror_r(errno, error_str, 1023));
668 static int open_file(struct pfiled *pfiled, char *target, uint32_t targetlen, int mode)
671 return open_file_write(pfiled, target, targetlen);
672 } else if (mode == READ) {
673 return open_file_read(pfiled, target, targetlen);
676 XSEGLOG2(&lc, E, "Invalid mode for target");
681 static int dir_open(struct pfiled *pfiled, struct fio *fio,
682 char *target, uint32_t targetlen, int mode)
685 struct fdcache_entry *e;
686 xcache_handler h = NoEntry, nh;
687 char name[XSEG_MAX_TARGETLEN + 1];
689 if (targetlen > XSEG_MAX_TARGETLEN) {
690 XSEGLOG2(&lc, E, "Invalid targetlen %u, max: %u",
691 targetlen, XSEG_MAX_TARGETLEN);
694 strncpy(name, target, targetlen);
696 XSEGLOG2(&lc, I, "Dir open started for %s", name);
698 h = xcache_lookup(&pfiled->cache, name);
700 r = is_target_valid_len(pfiled, target, targetlen, mode);
702 XSEGLOG2(&lc, E, "Invalid len for target %s", name);
706 h = xcache_alloc_init(&pfiled->cache, name);
708 /* FIXME add waitq to wait for free */
709 XSEGLOG2(&lc, E, "Could not allocate cache entry for %s",
713 XSEGLOG2(&lc, D, "Allocated new handler %llu for %s",
714 (long long unsigned)h, name);
716 e = xcache_get_entry(&pfiled->cache, h);
718 XSEGLOG2(&lc, E, "Alloced handler but no valid fd cache entry");
722 /* open/create file */
723 fd = open_file(pfiled, target, targetlen, mode);
725 XSEGLOG2(&lc, E, "Could not open file for target %s", name);
728 XSEGLOG2(&lc, D, "Opened file %s. fd %d", name, fd);
732 XSEGLOG2(&lc, D, "Inserting handler %llu for %s to fdcache",
733 (long long unsigned)h, name);
734 nh = xcache_insert(&pfiled->cache, h);
736 XSEGLOG2(&lc, D, "Partial cache hit for %s. New handler %llu",
737 name, (long long unsigned)nh);
738 xcache_put(&pfiled->cache, h);
742 XSEGLOG2(&lc, D, "Cache hit for %s, handler: %llu", name,
743 (long long unsigned)h);
746 e = xcache_get_entry(&pfiled->cache, h);
748 XSEGLOG2(&lc, E, "Found handler but no valid fd cache entry");
749 xcache_put(&pfiled->cache, h);
755 //assert e->fd != -1 ?;
756 XSEGLOG2(&lc, I, "Dir open finished for %s", name);
760 xcache_free_new(&pfiled->cache, h);
762 XSEGLOG2(&lc, E, "Dir open failed for %s", name);
766 static void handle_read(struct peerd *peer, struct peer_req *pr)
768 struct pfiled *pfiled = __get_pfiled(peer);
769 struct fio *fio = __get_fio(pr);
770 struct xseg_request *req = pr->req;
772 char *target = xseg_get_target(peer->xseg, req);
773 char *data = xseg_get_data(peer->xseg, req);
775 XSEGLOG2(&lc, I, "Handle read started for pr: %p, req: %p", pr, pr->req);
778 pfiled_complete(peer, pr);
782 if (req->datalen < req->size) {
783 XSEGLOG2(&lc, E, "Request datalen is less than request size");
784 pfiled_fail(peer, pr);
789 fd = dir_open(pfiled, fio, target, req->targetlen, READ);
791 if (errno != ENOENT) {
792 XSEGLOG2(&lc, E, "Open failed");
793 pfiled_fail(peer, pr);
796 memset(data, 0, req->size);
797 req->serviced = req->size;
803 XSEGLOG2(&lc, D, "req->serviced: %llu, req->size: %llu", req->serviced,
805 r = pfiled_read(pfiled, fd, data, req->size, req->offset);
807 XSEGLOG2(&lc, E, "Cannot read");
809 } else if (r < req->size) {
810 /* reached end of file. zero out the rest data buffer */
811 memset(data + r, 0, req->size - r);
812 req->serviced = req->size;
816 XSEGLOG2(&lc, D, "req->serviced: %llu, req->size: %llu", req->serviced,
820 if (req->serviced > 0 ) {
821 XSEGLOG2(&lc, I, "Handle read completed for pr: %p, req: %p",
823 pfiled_complete(peer, pr);
826 XSEGLOG2(&lc, E, "Handle read failed for pr: %p, req: %p",
828 pfiled_fail(peer, pr);
833 static void handle_write(struct peerd *peer, struct peer_req *pr)
835 struct pfiled *pfiled = __get_pfiled(peer);
836 struct fio *fio = __get_fio(pr);
837 struct xseg_request *req = pr->req;
840 char *target = xseg_get_target(peer->xseg, req);
841 char *data = xseg_get_data(peer->xseg, req);
843 XSEGLOG2(&lc, I, "Handle write started for pr: %p, req: %p", pr, pr->req);
845 if (req->datalen < req->size) {
846 XSEGLOG2(&lc, E, "Request datalen is less than request size");
847 pfiled_fail(peer, pr);
851 fd = dir_open(pfiled, fio, target, req->targetlen, WRITE);
853 XSEGLOG2(&lc, E, "Open failed");
854 pfiled_fail(peer, pr);
859 if (req->flags & (XF_FLUSH | XF_FUA)) {
860 /* No FLUSH/FUA support yet (O_SYNC ?).
861 * note that with FLUSH/size == 0
862 * there will probably be a (uint64_t)-1 offset */
863 pfiled_complete(peer, pr);
866 pfiled_complete(peer, pr);
871 XSEGLOG2(&lc, D, "req->serviced: %llu, req->size: %llu", req->serviced,
873 r = pfiled_write(pfiled, fd, data, req->size, req->offset);
879 XSEGLOG2(&lc, D, "req->serviced: %llu, req->size: %llu", req->serviced,
883 XSEGLOG2(&lc, E, "Fsync failed.");
884 /* if fsync fails, then no bytes serviced correctly */
888 if (req->serviced > 0 ) {
889 XSEGLOG2(&lc, I, "Handle write completed for pr: %p, req: %p",
891 pfiled_complete(peer, pr);
894 XSEGLOG2(&lc, E, "Handle write failed for pr: %p, req: %p",
896 pfiled_fail(peer, pr);
901 static void handle_info(struct peerd *peer, struct peer_req *pr)
903 struct pfiled *pfiled = __get_pfiled(peer);
904 struct fio *fio = __get_fio(pr);
905 struct xseg_request *req = pr->req;
909 char *target = xseg_get_target(peer->xseg, req);
910 char *data = xseg_get_data(peer->xseg, req);
911 char buf[XSEG_MAX_TARGETLEN + 1];
912 struct xseg_reply_info *xinfo = (struct xseg_reply_info *)data;
914 if (req->datalen < sizeof(struct xseg_reply_info)) {
915 strncpy(buf, target, req->targetlen);
916 r = xseg_resize_request(peer->xseg, req, req->targetlen, sizeof(struct xseg_reply_info));
918 XSEGLOG2(&lc, E, "Cannot resize request");
919 pfiled_fail(peer, pr);
922 target = xseg_get_target(peer->xseg, req);
923 strncpy(target, buf, req->targetlen);
926 XSEGLOG2(&lc, I, "Handle info started for pr: %p, req: %p", pr, pr->req);
927 fd = dir_open(pfiled, fio, target, req->targetlen, READ);
929 XSEGLOG2(&lc, E, "Dir open failed");
930 pfiled_fail(peer, pr);
934 r = fstat(fd, &stat);
936 XSEGLOG2(&lc, E, "fail in stat");
937 pfiled_fail(peer, pr);
941 size = (uint64_t)stat.st_size;
944 XSEGLOG2(&lc, I, "Handle info completed for pr: %p, req: %p", pr, pr->req);
945 pfiled_complete(peer, pr);
948 static void handle_copy(struct peerd *peer, struct peer_req *pr)
950 struct pfiled *pfiled = __get_pfiled(peer);
951 struct fio *fio = __get_fio(pr);
952 struct xseg_request *req = pr->req;
953 char *target = xseg_get_target(peer->xseg, req);
954 char *data = xseg_get_data(peer->xseg, req);
955 struct xseg_request_copy *xcopy = (struct xseg_request_copy *)data;
957 char *buf = malloc(MAX_PATH_SIZE + MAX_FILENAME_SIZE);
958 int src = -1, dst = -1, r = -1;
959 ssize_t c = 0, bytes;
962 XSEGLOG2(&lc, I, "Handle copy started for pr: %p, req: %p", pr, pr->req);
964 XSEGLOG2(&lc, E, "Out of memory");
965 pfiled_fail(peer, pr);
969 r = is_target_valid_len(pfiled, xcopy->target, xcopy->targetlen, READ);
971 XSEGLOG2(&lc, E, "Source target not valid");
975 dst = dir_open(pfiled, fio, target, req->targetlen, WRITE);
977 XSEGLOG2(&lc, E, "Fail in dst");
982 src = open_file(pfiled, xcopy->target, xcopy->targetlen, READ);
984 XSEGLOG2(&lc, E, "Failed to open src");
990 XSEGLOG2(&lc, E, "fail in stat for src %s", buf);
996 limit = min(req->size, st.st_size);
998 bytes = sendfile(dst, src, NULL, limit - c);
1000 XSEGLOG2(&lc, E, "Copy failed for %s", buf);
1010 if (limit && c == limit)
1011 req->serviced = req->size;
1017 XSEGLOG2(&lc, E, "Handle copy failed for pr: %p, req: %p", pr, pr->req);
1018 pfiled_fail(peer, pr);
1020 XSEGLOG2(&lc, I, "Handle copy completed for pr: %p, req: %p", pr, pr->req);
1021 pfiled_complete(peer, pr);
1026 static void handle_delete(struct peerd *peer, struct peer_req *pr)
1028 struct pfiled *pfiled = __get_pfiled(peer);
1029 //struct fio *fio = __get_fio(pr);
1030 struct xseg_request *req = pr->req;
1031 char name[XSEG_MAX_TARGETLEN + 1];
1032 char *buf = malloc(MAX_PATH_SIZE + MAX_FILENAME_SIZE);
1034 char *target = xseg_get_target(peer->xseg, req);
1036 XSEGLOG2(&lc, I, "Handle delete started for pr: %p, req: %p", pr, pr->req);
1039 XSEGLOG2(&lc, E, "Out of memory");
1040 pfiled_fail(peer, pr);
1044 r = is_target_valid_len(pfiled, target, req->targetlen, READ);
1046 XSEGLOG2(&lc, E, "Target not valid");
1050 r = create_path(buf, pfiled, target, req->targetlen, 0);
1052 XSEGLOG2(&lc, E, "Create path failed");
1059 XSEGLOG2(&lc, E, "Handle delete failed for pr: %p, req: %p", pr, pr->req);
1060 pfiled_fail(peer, pr);
1062 strncpy(name, target, XSEG_MAX_TARGETLEN);
1063 name[XSEG_MAX_TARGETLEN] = 0;
1064 xcache_invalidate(&pfiled->cache, name);
1065 XSEGLOG2(&lc, I, "Handle delete completed for pr: %p, req: %p", pr, pr->req);
1066 pfiled_complete(peer, pr);
1071 static int __get_precalculated_hash(struct peerd *peer, char *target,
1072 uint32_t targetlen, char *hash)
1076 uint32_t len, hash_file_len;
1077 char *hash_file = NULL;
1078 struct pfiled *pfiled = __get_pfiled(peer);
1080 XSEGLOG2(&lc, D, "Started.");
1082 hash_file = malloc(MAX_FILENAME_SIZE + 1);
1083 hash_file_len = strjoin(hash_file, target, targetlen, HASH_SUFFIX, HASH_SUFFIX_LEN);
1086 r = pfiled_read_name(pfiled, hash_file, hash_file_len, hash, HEXLIFIED_SHA256_DIGEST_SIZE, 0);
1088 if (errno != ENOENT){
1089 XSEGLOG2(&lc, E, "Error opening %s", hash_file);
1091 XSEGLOG2(&lc, I, "No precalculated hash for %s", hash_file);
1097 XSEGLOG2(&lc, D, "Read %u bytes", len);
1099 if (len == HEXLIFIED_SHA256_DIGEST_SIZE){
1100 hash[HEXLIFIED_SHA256_DIGEST_SIZE] = 0;
1101 XSEGLOG2(&lc, D, "Found hash for %s : %s", hash_file, hash);
1106 XSEGLOG2(&lc, D, "Finished.");
1110 static int __set_precalculated_hash(struct peerd *peer, char *target,
1111 uint32_t targetlen, char *hash)
1115 uint32_t len, hash_file_len;
1116 char *hash_file = NULL;
1117 struct pfiled *pfiled = __get_pfiled(peer);
1119 XSEGLOG2(&lc, D, "Started.");
1121 hash_file = malloc(MAX_FILENAME_SIZE + 1);
1122 hash_file_len = strjoin(hash_file, target, targetlen, HASH_SUFFIX, HASH_SUFFIX_LEN);
1124 r = pfiled_write_name(pfiled, hash_file, hash_file_len, hash, HEXLIFIED_SHA256_DIGEST_SIZE, 0,
1125 O_CREAT|O_EXCL, S_IWUSR|S_IRUSR);
1127 if (errno != EEXIST){
1128 XSEGLOG2(&lc, E, "Error opening %s", hash_file);
1130 XSEGLOG2(&lc, I, "Hash file already exists %s", hash_file);
1137 XSEGLOG2(&lc, D, "Wrote %u bytes", len);
1141 XSEGLOG2(&lc, D, "Finished.");
1145 static void handle_hash(struct peerd *peer, struct peer_req *pr)
1150 //stat (open without create)
1151 //write to hash_tmpfile
1155 int src = -1, dst = -1, r = -1;
1157 uint64_t sum, trailing_zeros;
1158 struct pfiled *pfiled = __get_pfiled(peer);
1159 struct fio *fio = __get_fio(pr);
1160 struct xseg_request *req = pr->req;
1161 char *pathname = NULL, *tmpfile_pathname = NULL, *tmpfile = NULL;
1163 // char hash_name[HEXLIFIED_SHA256_DIGEST_SIZE + 1];
1165 char name[XSEG_MAX_TARGETLEN + 1];
1167 unsigned char *object_data = NULL;
1168 unsigned char sha[SHA256_DIGEST_SIZE];
1169 struct xseg_reply_hash *xreply;
1171 target = xseg_get_target(peer->xseg, req);
1173 XSEGLOG2(&lc, I, "Handle hash started for pr: %p, req: %p",
1177 XSEGLOG2(&lc, E, "No request size provided");
1182 r = is_target_valid_len(pfiled, target, req->targetlen, READ);
1184 XSEGLOG2(&lc, E, "Source target not valid");
1188 r = posix_memalign(&hash_name, 512, 512 + 1);
1190 r = __get_precalculated_hash(peer, target, req->targetlen, hash_name);
1192 XSEGLOG2(&lc, E, "Error getting precalculated hash");
1196 if (hash_name[0] != 0) {
1197 XSEGLOG2(&lc, I, "Precalucated hash found %s", hash_name);
1201 XSEGLOG2(&lc, I, "No precalculated hash found");
1203 strncpy(name, target, req->targetlen);
1204 name[req->targetlen] = 0;
1206 pathname = malloc(MAX_PATH_SIZE + MAX_FILENAME_SIZE + 1);
1207 //object_data = malloc(sizeof(char) * req->size);
1208 r = posix_memalign(&object_data, 512, sizeof(char) * req->size);
1209 if (!pathname || !object_data){
1210 XSEGLOG2(&lc, E, "Out of memory");
1214 src = dir_open(pfiled, fio, target, req->targetlen, READ);
1216 XSEGLOG2(&lc, E, "Fail in src");
1221 c = pfiled_read(pfiled, src, object_data, req->size, req->offset);
1223 XSEGLOG2(&lc, E, "Error reading from source");
1229 //rstrip here in case zeros were written in the end
1231 for (;trailing_zeros < sum; trailing_zeros++)
1232 if (object_data[sum - trailing_zeros - 1])
1235 XSEGLOG2(&lc, D, "Read %llu, Trainling zeros %llu",
1236 sum, trailing_zeros);
1238 sum -= trailing_zeros;
1239 //calculate hash name
1240 SHA256(object_data, sum, sha);
1242 hexlify(sha, SHA256_DIGEST_SIZE, hash_name);
1243 hash_name[HEXLIFIED_SHA256_DIGEST_SIZE] = 0;
1246 r = create_path(pathname, pfiled, hash_name, HEXLIFIED_SHA256_DIGEST_SIZE, 1);
1248 XSEGLOG2(&lc, E, "Create path failed");
1254 dst = open_file(pfiled, hash_name, HEXLIFIED_SHA256_DIGEST_SIZE, READ);
1256 XSEGLOG2(&lc, I, "%s already exists, no write needed", pathname);
1257 req->serviced = req->size;
1262 tmpfile_pathname = malloc(MAX_PATH_SIZE + MAX_FILENAME_SIZE + 1);
1263 if (!tmpfile_pathname){
1264 XSEGLOG2(&lc, E, "Out of memory");
1269 tmpfile = malloc(MAX_FILENAME_SIZE);
1271 XSEGLOG2(&lc, E, "Out of memory");
1276 len = strnjoin(tmpfile, 4, target, req->targetlen,
1277 HASH_SUFFIX, HASH_SUFFIX_LEN,
1278 pfiled->uniquestr, pfiled->uniquestr_len,
1279 fio->str_id, FIO_STR_ID_LEN);
1281 r = pfiled_write_name(pfiled, tmpfile, len, object_data, sum, 0, O_CREAT|O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
1283 if (errno != EEXIST){
1284 char error_str[1024];
1285 XSEGLOG2(&lc, E, "Error opening %s (%s)", tmpfile_pathname, strerror_r(errno, error_str, 1023));
1287 XSEGLOG2(&lc, E, "Error opening %s. Stale data found.",
1292 } else if (r < sum) {
1293 XSEGLOG2(&lc, E, "Error writting to dst file %s", tmpfile_pathname);
1297 XSEGLOG2(&lc, D, "Opened %s and wrote", tmpfile);
1299 r = create_path(tmpfile_pathname, pfiled, tmpfile, len, 1);
1301 XSEGLOG2(&lc, E, "Create path failed");
1306 r = link(tmpfile_pathname, pathname);
1307 if (r < 0 && errno != EEXIST) {
1308 XSEGLOG2(&lc, E, "Error linking tmp file %s. Errno %d",
1314 r = unlink(tmpfile_pathname);
1316 XSEGLOG2(&lc, W, "Error unlinking tmp file %s", tmpfile_pathname);
1320 r = __set_precalculated_hash(peer, target, req->targetlen, hash_name);
1322 XSEGLOG2(&lc, W, "Error setting precalculated hash");
1327 r = xseg_resize_request(peer->xseg, pr->req, pr->req->targetlen,
1328 sizeof(struct xseg_reply_hash));
1330 XSEGLOG2(&lc, E, "Resize request failed");
1335 xreply = (struct xseg_reply_hash *)xseg_get_data(peer->xseg, req);
1336 strncpy(xreply->target, hash_name, HEXLIFIED_SHA256_DIGEST_SIZE);
1337 xreply->targetlen = HEXLIFIED_SHA256_DIGEST_SIZE;
1339 req->serviced = req->size;
1347 XSEGLOG2(&lc, E, "Handle hash failed for pr: %p, req: %p. ",
1348 "Target %s", pr, pr->req, name);
1349 pfiled_fail(peer, pr);
1351 XSEGLOG2(&lc, I, "Handle hash completed for pr: %p, req: %p\n\t"
1352 "hashed %s to %s", pr, pr->req, name, hash_name);
1353 pfiled_complete(peer, pr);
1355 free(tmpfile_pathname);
1361 unlink(tmpfile_pathname);
1365 static int __locked_by(char *lockfile, char *expected, uint32_t expected_len, int direct)
1370 char tmpbuf[MAX_UNIQUESTR_LEN];
1372 XSEGLOG2(&lc, D, "Started. Lockfile: %s, expected: %s, expected_len: %u", lockfile, expected, expected_len);
1373 r = read_path(lockfile, tmpbuf, MAX_UNIQUESTR_LEN, 0, direct);
1375 if (errno != ENOENT){
1376 XSEGLOG2(&lc, E, "Error opening %s", lockfile);
1379 XSEGLOG2(&lc, I, "lock file removed");
1385 XSEGLOG2(&lc, D, "Read %u bytes", len);
1386 if (!strncmp(tmpbuf, expected, expected_len)){
1387 XSEGLOG2(&lc, D, "Lock file %s locked by us.", lockfile);
1391 XSEGLOG2(&lc, D, "Finished. Lockfile: %s", lockfile);
1395 static int __try_lock(struct pfiled *pfiled, char *tmpfile, char *lockfile,
1396 uint32_t flags, int fd)
1399 XSEGLOG2(&lc, D, "Started. Lockfile: %s, Tmpfile:%s", lockfile, tmpfile);
1401 r = pfiled_write(pfiled, fd, pfiled->uniquestr, pfiled->uniquestr_len, 0);
1402 if (r < 0 || r < pfiled->uniquestr_len) {
1410 direct = pfiled->directio;
1413 while (link(tmpfile, lockfile) < 0) {
1415 if (errno != EEXIST){
1416 XSEGLOG2(&lc, E, "Error linking %s to %s",
1420 r = __locked_by(lockfile, pfiled->uniquestr, pfiled->uniquestr_len, direct);
1424 if (flags & XF_NOSYNC) {
1425 XSEGLOG2(&lc, D, "Could not get lock file %s, "
1426 "XF_NOSYNC set. Aborting", lockfile);
1431 XSEGLOG2(&lc, D, "Finished. Lockfile: %s", lockfile);
1435 static void handle_acquire(struct peerd *peer, struct peer_req *pr)
1438 struct pfiled *pfiled = __get_pfiled(peer);
1439 struct fio *fio = __get_fio(pr);
1440 struct xseg_request *req = pr->req;
1441 char *buf = malloc(MAX_FILENAME_SIZE);
1442 char *tmpfile = malloc(MAX_FILENAME_SIZE);
1443 char *lockfile_pathname = malloc(MAX_PATH_SIZE + MAX_FILENAME_SIZE);
1444 char *tmpfile_pathname = malloc(MAX_PATH_SIZE + MAX_FILENAME_SIZE);
1446 char *target = xseg_get_target(peer->xseg, req);
1447 uint32_t buf_len, tmpfile_len;
1449 if (!buf || !tmpfile_pathname || !lockfile_pathname) {
1450 XSEGLOG2(&lc, E, "Out of memory");
1451 pfiled_fail(peer, pr);
1455 r = is_target_valid_len(pfiled, target, req->targetlen, READ);
1457 XSEGLOG2(&lc, E, "Target not valid");
1462 buf_len = strjoin(buf, target, req->targetlen, LOCK_SUFFIX, LOCK_SUFFIX_LEN);
1464 XSEGLOG2(&lc, I, "Started. Lockfile: %s", buf);
1467 tmpfile_len = strnjoin(tmpfile, 3, buf, buf_len,
1468 pfiled->uniquestr, pfiled->uniquestr_len,
1469 fio->str_id, FIO_STR_ID_LEN);
1471 XSEGLOG2(&lc, I, "Trying to acquire lock %s", buf);
1473 if (create_path(tmpfile_pathname, pfiled, tmpfile, tmpfile_len, 1) < 0) {
1474 XSEGLOG2(&lc, E, "Create path failed for %s", buf);
1478 if (create_path(lockfile_pathname, pfiled, buf, buf_len, 1) < 0) {
1479 XSEGLOG2(&lc, E, "Create path failed for %s", buf);
1483 //create exclusive unique lockfile (block_uniqueid+target)
1485 // write blocker uniqueid to the unique lockfile
1486 // try to link it to the lockfile
1488 // unlink unique lockfile;
1491 // spin while not able to link
1494 XSEGLOG2(&lc, D, "Tmpfile: %s", tmpfile_pathname);
1495 flags = O_RDWR|O_CREAT|O_EXCL;
1496 if (pfiled->directio)
1498 fd = open(tmpfile_pathname, flags, S_IRWXU | S_IRUSR);
1501 if (errno != EEXIST){
1502 XSEGLOG2(&lc, E, "Error opening %s", tmpfile_pathname);
1505 XSEGLOG2(&lc, E, "Error opening %s. Stale data found.",
1510 XSEGLOG2(&lc, D, "Tmpfile %s created. Trying to get lock",
1512 r = __try_lock(pfiled, tmpfile_pathname, lockfile_pathname,
1515 XSEGLOG2(&lc, E, "Trying to get lock %s failed", buf);
1518 XSEGLOG2(&lc, D, "Trying to get lock %s succeed", buf);
1523 XSEGLOG2(&lc, W, "Error closing %s", tmpfile_pathname);
1525 r = unlink(tmpfile_pathname);
1527 XSEGLOG2(&lc, E, "Error unlinking %s", tmpfile_pathname);
1532 XSEGLOG2(&lc, I, "Failed to acquire lock %s", buf);
1533 pfiled_fail(peer, pr);
1536 XSEGLOG2(&lc, I, "Acquired lock %s", buf);
1537 pfiled_complete(peer, pr);
1540 free(lockfile_pathname);
1541 free(tmpfile_pathname);
1545 static void handle_release(struct peerd *peer, struct peer_req *pr)
1547 struct pfiled *pfiled = __get_pfiled(peer);
1548 // struct fio *fio = __get_fio(pr);
1549 struct xseg_request *req = pr->req;
1550 char *buf = malloc(MAX_FILENAME_SIZE + 1);
1551 char *pathname = malloc(MAX_PATH_SIZE + MAX_FILENAME_SIZE + 1);
1552 char *tmpbuf = malloc(MAX_UNIQUESTR_LEN + 1);
1553 char *target = xseg_get_target(peer->xseg, req);
1554 int r, buf_len, direct;
1556 if (!buf || !pathname) {
1557 XSEGLOG2(&lc, E, "Out of memory");
1562 r = is_target_valid_len(pfiled, target, req->targetlen, READ);
1564 XSEGLOG2(&lc, E, "Target not valid");
1568 buf_len = strnjoin(buf, 2 , target, req->targetlen, LOCK_SUFFIX, LOCK_SUFFIX_LEN);
1570 XSEGLOG2(&lc, I, "Started. Lockfile: %s", buf);
1572 r = create_path(pathname, pfiled, buf, buf_len, 0);
1574 XSEGLOG2(&lc, E, "Create path failed for %s", buf);
1578 direct = pfiled->directio;
1580 if ((req->flags & XF_FORCE) || !__locked_by(pathname, pfiled->uniquestr,
1581 pfiled->uniquestr_len, direct)) {
1582 r = unlink(pathname);
1584 XSEGLOG2(&lc, E, "Could not unlink %s", pathname);
1596 XSEGLOG2(&lc, I, "Released lockfile: %s", buf);
1599 XSEGLOG2(&lc, I, "Finished. Lockfile: %s", buf);
1606 int dispatch(struct peerd *peer, struct peer_req *pr, struct xseg_request *req,
1607 enum dispatch_reason reason)
1609 struct fio *fio = __get_fio(pr);
1610 if (reason == dispatch_accept)
1615 handle_read(peer, pr); break;
1617 handle_write(peer, pr); break;
1619 handle_info(peer, pr); break;
1621 handle_copy(peer, pr); break;
1623 handle_delete(peer, pr); break;
1625 handle_acquire(peer, pr); break;
1627 handle_release(peer, pr); break;
1629 handle_hash(peer, pr); break;
1632 handle_unknown(peer, pr);
1637 int custom_peer_init(struct peerd *peer, int argc, char *argv[])
1640 get blocks,maps paths
1641 get optional pithos block,maps paths
1643 check if greater than limit (tip: getrlimit)
1644 assert cachesize greater than nr_ops
1645 assert nr_ops greater than nr_threads
1652 struct pfiled *pfiled = malloc(sizeof(struct pfiled));
1654 struct xcache_ops c_ops = {
1655 .on_node_init = cache_node_init,
1656 .on_init = cache_init,
1657 .on_put = cache_put,
1660 XSEGLOG2(&lc, E, "Out of memory");
1664 peer->priv = pfiled;
1666 pfiled->maxfds = 2 * peer->nr_ops;
1668 for (i = 0; i < peer->nr_ops; i++) {
1669 peer->peer_reqs[i].priv = malloc(sizeof(struct fio));
1670 if (!peer->peer_reqs->priv){
1671 XSEGLOG2(&lc, E, "Out of memory");
1675 fio = __get_fio(&peer->peer_reqs[i]);
1676 fio->str_id[0] = '_';
1677 fio->str_id[1] = 'a' + (i / 26);
1678 fio->str_id[2] = 'a' + (i % 26);
1681 pfiled->vpath[0] = 0;
1682 pfiled->prefix[0] = 0;
1683 pfiled->uniquestr[0] = 0;
1685 BEGIN_READ_ARGS(argc, argv);
1686 READ_ARG_ULONG("--fdcache", pfiled->maxfds);
1687 READ_ARG_STRING("--archip", pfiled->vpath, MAX_PATH_SIZE);
1688 READ_ARG_STRING("--prefix", pfiled->prefix, MAX_PREFIX_LEN);
1689 READ_ARG_STRING("--uniquestr", pfiled->uniquestr, MAX_UNIQUESTR_LEN);
1690 READ_ARG_BOOL("--directio", pfiled->directio);
1693 pfiled->uniquestr_len = strlen(pfiled->uniquestr);
1694 pfiled->prefix_len = strlen(pfiled->prefix);
1696 //TODO test path exist/is_dir/have_access
1697 pfiled->vpath_len = strlen(pfiled->vpath);
1698 if (!pfiled->vpath_len){
1699 XSEGLOG2(&lc, E, "Archipelago path was not provided");
1703 if (pfiled->vpath[pfiled->vpath_len -1] != '/'){
1704 pfiled->vpath[pfiled->vpath_len] = '/';
1705 pfiled->vpath[++pfiled->vpath_len]= 0;
1708 r = getrlimit(RLIMIT_NOFILE, &rlim);
1710 XSEGLOG2(&lc, E, "Could not get limit for max fds");
1713 //TODO check nr_ops == nr_threads.
1715 r = xcache_init(&pfiled->cache, pfiled->maxfds, &c_ops, XCACHE_LRU_HEAP, peer);
1718 //check max fds. (> fdcache + nr_threads)
1719 //TODO assert fdcache > 2*nr_threads or add waitq
1720 if (rlim.rlim_cur < pfiled->cache.size + peer->nr_threads - 4) {
1721 XSEGLOG2(&lc, E, "FD limit %d is less than cachesize + nr_ops -4(%u)",
1722 rlim.rlim_cur, pfiled->cache.size + peer->nr_ops - 4);
1730 void custom_peer_finalize(struct peerd *peer)
1733 we could close all fds, but we can let the system do it for us.
1739 static int safe_atoi(char *s)
1744 l = strtol(s, &endp, 10);
1745 if (s != endp && *endp == '\0')