diff options
author | Stefan Behrens <sbehrens@giantdisaster.de> | 2013-03-26 17:31:14 +0100 |
---|---|---|
committer | Stefan Behrens <sbehrens@giantdisaster.de> | 2013-04-25 11:16:05 +0200 |
commit | cb0e413a80c9a9347159a4ecae31acfd2ea9f87c (patch) | |
tree | 4f2ed2e939886ec118c9b9080501ea8933d1b3dc | |
parent | 89ebe6178641edd41033661ba9ac036545b4f96e (diff) | |
download | far-progs-cb0e413a80c9a9347159a4ecae31acfd2ea9f87c.tar.gz |
far-rcv: add far-rcv lib
Signed-off-by: Stefan Behrens <sbehrens@giantdisaster.de>
-rw-r--r-- | far-rcv/Makefile | 38 | ||||
-rw-r--r-- | far-rcv/far-crc32c.c | 145 | ||||
-rw-r--r-- | far-rcv/far-crc32c.h | 47 | ||||
-rw-r--r-- | far-rcv/far-endian.c | 86 | ||||
-rw-r--r-- | far-rcv/far-endian.h | 50 | ||||
-rw-r--r-- | far-rcv/far-rcv.c | 1303 | ||||
-rw-r--r-- | far-rcv/far-rcv.h | 104 | ||||
-rw-r--r-- | far-rcv/far-xattr.c | 124 | ||||
-rw-r--r-- | far-rcv/far-xattr.h | 38 |
9 files changed, 1935 insertions, 0 deletions
diff --git a/far-rcv/Makefile b/far-rcv/Makefile new file mode 100644 index 0000000..d3bb494 --- /dev/null +++ b/far-rcv/Makefile @@ -0,0 +1,38 @@ +OS = $(shell uname -s) +RELEASE = $(shell uname -r) + +ifeq "$(OS)" "Linux" +CFLAGS = -D__LINUX__ +CFLAGS += -DHAVE_NTOHLL +CFLAGS += -DHAVE_UTIMENSAT +else +ifeq "$(OS)" "SunOS" +CFLAGS = -D__SOLARIS__ +ifeq "$(RELEASE)" "5.11" +CFLAGS += -DHAVE_NTOHLL +CFLAGS += -DHAVE_UTIMENSAT +endif +endif +endif + +CC = gcc +CFLAGS += -O2 -g -Wall -Werror +DEPFLAGS = -Wp,-MMD,$(@D)/.$(@F).d,-MT,$@ +LDFLAGS = $(LIBS) +AR = ar +ARFLAGS = rc +OBJECTS = far-rcv.o far-crc32c.o far-endian.o far-xattr.o +TARGET_LIB = far-rcv.a + +all: $(TARGET_LIB) + +$(TARGET_LIB): $(OBJECTS) + $(AR) $(ARFLAGS) $@ $(OBJECTS) + +.c.o: + $(CC) $(DEPFLAGS) $(CFLAGS) -c $< + +clean: + -rm -rf $(TARGET_LIB) $(OBJECTS) .*.d + +-include .*.d diff --git a/far-rcv/far-crc32c.c b/far-rcv/far-crc32c.c new file mode 100644 index 0000000..abd9c8a --- /dev/null +++ b/far-rcv/far-crc32c.c @@ -0,0 +1,145 @@ +/* + * This file is derived from work of Ross N. Williams, which is described in + * the document "A Painless Guide to CRC Error Detection Algorithms". + * The document can be found here: + * <http://www.ross.net/crc/download/crc_v3.txt> + * + * It contains the following copyright and status information: + * ----- quote start ----- + * Version : 3. + * Date : 19 August 1993. + * Author : Ross N. Williams. + * Net : ross@guest.adelaide.edu.au. + * FTP : ftp.adelaide.edu.au/pub/rocksoft/crc_v3.txt + * Company : Rocksoft^tm Pty Ltd. + * Snail : 16 Lerwick Avenue, Hazelwood Park 5066, Australia. + * Fax : +61 8 373-4911 (c/- Internode Systems Pty Ltd). + * Phone : +61 8 379-9217 (10am to 10pm Adelaide Australia time). + * Note : "Rocksoft" is a trademark of Rocksoft Pty Ltd, Australia. + * Status : Copyright (C) Ross Williams, 1993. However, permission is + * granted to make and distribute verbatim copies of this + * document provided that this information block and copyright + * notice is included. Also, the C code modules included + * in this document are fully public domain. + * Thanks : Thanks to Jean-loup Gailly (jloup@chorus.fr) and Mark Adler + * (me@quest.jpl.nasa.gov) who both proof read this document + * and picked out lots of nits as well as some big fat bugs. + * ----- quote end ----- + * + * The CRC lookup table is generated by using code from the aforementioned + * document of Ross N. Williams. The function crc32c_calc() is a slightly + * modified copy of the function crc_reflected() from the mentioned document. + * The modifications and the modified code are fully public domain. + */ + +/* + * This file contains a function to calculate the CRC-32C. + * These functions are used internally in the far-rcv library. + */ + +#include <stdint.h> +#include <stdlib.h> + +#define INIT_REFLECTED 0UL +#define XOROT 0UL + + +/*****************************************************************/ +/* */ +/* CRC LOOKUP TABLE */ +/* ================ */ +/* The following CRC lookup table was generated automagically */ +/* by the Rocksoft^tm Model CRC Algorithm Table Generation */ +/* Program V1.0 using the following model parameters: */ +/* */ +/* Width : 4 bytes. */ +/* Poly : 0x1EDC6F41L */ +/* Reverse : TRUE. */ +/* */ +/* For more information on the Rocksoft^tm Model CRC Algorithm, */ +/* see the document titled "A Painless Guide to CRC Error */ +/* Detection Algorithms" by Ross Williams */ +/* (ross@guest.adelaide.edu.au.). This document is likely to be */ +/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft". */ +/* */ +/*****************************************************************/ + +static uint32_t crctable[256] = { + 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, + 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, + 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, + 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, + 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL, + 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, + 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, + 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL, + 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL, + 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, + 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, + 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, + 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, + 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL, + 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, + 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, + 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, + 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L, + 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, + 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L, + 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, + 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, + 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L, + 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, + 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, + 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, + 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, + 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, + 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L, + 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, + 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, + 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, + 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, + 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L, + 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L, + 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, + 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, + 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL, + 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, + 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, + 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, + 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, + 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL, + 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L, + 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, + 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, + 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, + 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, + 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, + 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL, + 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, + 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, + 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL, + 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L, + 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, + 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, + 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, + 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, + 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L, + 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, + 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, + 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, + 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, + 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L +}; + +/*****************************************************************/ +/* End of CRC Lookup Table */ +/*****************************************************************/ + +uint32_t far_crc32c_calc(uint32_t crc, const unsigned char *blk_adr, + size_t blk_len) +{ + while (blk_len--) + crc = crctable[(crc ^ *blk_adr++) & 0xFFL] ^ (crc >> 8); + + return crc ^ XOROT; +} diff --git a/far-rcv/far-crc32c.h b/far-rcv/far-crc32c.h new file mode 100644 index 0000000..8279523 --- /dev/null +++ b/far-rcv/far-crc32c.h @@ -0,0 +1,47 @@ +/* + * This file is derived from work of Ross N. Williams, which is described in + * the document "A Painless Guide to CRC Error Detection Algorithms". + * The document can be found here: + * <http://www.ross.net/crc/download/crc_v3.txt> + * + * It contains the following copyright and status information: + * ----- quote start ----- + * Version : 3. + * Date : 19 August 1993. + * Author : Ross N. Williams. + * Net : ross@guest.adelaide.edu.au. + * FTP : ftp.adelaide.edu.au/pub/rocksoft/crc_v3.txt + * Company : Rocksoft^tm Pty Ltd. + * Snail : 16 Lerwick Avenue, Hazelwood Park 5066, Australia. + * Fax : +61 8 373-4911 (c/- Internode Systems Pty Ltd). + * Phone : +61 8 379-9217 (10am to 10pm Adelaide Australia time). + * Note : "Rocksoft" is a trademark of Rocksoft Pty Ltd, Australia. + * Status : Copyright (C) Ross Williams, 1993. However, permission is + * granted to make and distribute verbatim copies of this + * document provided that this information block and copyright + * notice is included. Also, the C code modules included + * in this document are fully public domain. + * Thanks : Thanks to Jean-loup Gailly (jloup@chorus.fr) and Mark Adler + * (me@quest.jpl.nasa.gov) who both proof read this document + * and picked out lots of nits as well as some big fat bugs. + * ----- quote end ----- + * + * The function crc32c_calc() is a slightly modified copy of the function + * crc_reflected() from the mentioned document. + * The modifications and the modified code are fully public domain. + */ + +/* + * This file contains a function to calculate the CRC-32C. + * These functions are used internally in the far-rcv library. + */ + +#if !defined (__FAR_RCV_CRC32C_H) +#define __FAR_RCV_CRC32C_H + +#include <stdint.h> + +uint32_t far_crc32c_calc(uint32_t crc, const unsigned char *blk_adr, + size_t blk_len); + +#endif /* !defined (__FAR_RCV_CRC32C_H) */ diff --git a/far-rcv/far-endian.c b/far-rcv/far-endian.c new file mode 100644 index 0000000..a53ffad --- /dev/null +++ b/far-rcv/far-endian.c @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2012, 2013 STRATO AG. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/* + * Some portable functions to convert little endian numbers to the + * host byte order. + * It was not a goal to make these functions fast. + * These functions are used internally in the far-rcv library. + */ + +#include <sys/types.h> +#include <netinet/in.h> +#include <inttypes.h> +#include "far-endian.h" + + +#if !defined (__LINUX__) +static uint16_t far_rcv_le16tobe(uint16_t val); +static uint32_t far_rcv_le32tobe(uint32_t val); +static uint64_t far_rcv_le64tobe(uint64_t val); + + +uint16_t far_rcv_le16toh(uint16_t val) +{ + return ntohs(far_rcv_le16tobe(val)); +} + +uint32_t far_rcv_le32toh(uint32_t val) +{ + return ntohl(far_rcv_le32tobe(val)); +} + +uint64_t far_rcv_le64toh(uint64_t val) +{ +#if defined (HAVE_NTOHLL) + return ntohll(far_rcv_le64tobe(val)); +#else /* !defined (HAVE_NTOHLL) */ + if (ntohs(0xbabe) != 0xbabe) + return val; + else + return far_rcv_le64tobe(val); +#endif /* !defined (HAVE_NTOHLL) */ +} + +static uint16_t far_rcv_le16tobe(uint16_t val) +{ + return ((uint16_t)(val << 8)) | (val >> 8); +} + +static uint32_t far_rcv_le32tobe(uint32_t val) +{ + uint16_t lower = (uint16_t)val; + uint16_t upper = (uint16_t)(val >> 16); + + return (((uint32_t)far_rcv_le16tobe(lower)) << 16) | + (uint32_t)far_rcv_le16tobe(upper); +} + +static uint64_t far_rcv_le64tobe(uint64_t val) +{ + uint32_t lower = (uint32_t)val; + uint32_t upper = (uint32_t)(val >> 32); + + return (((uint64_t)far_rcv_le32tobe(lower)) << 32) | + (uint64_t)far_rcv_le32tobe(upper); +} +#endif /* !defined (__LINUX__) */ diff --git a/far-rcv/far-endian.h b/far-rcv/far-endian.h new file mode 100644 index 0000000..611d9cf --- /dev/null +++ b/far-rcv/far-endian.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012, 2013 STRATO AG. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/* + * Some portable functions to convert little endian numbers to the + * host byte order. + * It was not a goal to make these functions fast. + * These functions are used internally in the far-rcv library. + */ + +#if !defined (__FAR_RCV_ENDIAN_H) +#define __FAR_RCV_ENDIAN_H + +#include <stdint.h> + + +#if defined (__LINUX__) +#include <endian.h> + +#define far_rcv_le16toh le16toh +#define far_rcv_le32toh le32toh +#define far_rcv_le64toh le64toh + +#else /* !defined (__LINUX__) */ + +uint16_t far_rcv_le16toh(uint16_t val); +uint32_t far_rcv_le32toh(uint32_t val); +uint64_t far_rcv_le64toh(uint64_t val); +#endif /* !defined (__LINUX__) */ + +#endif /* !defined (__FAR_RCV_ENDIAN_H) */ diff --git a/far-rcv/far-rcv.c b/far-rcv/far-rcv.c new file mode 100644 index 0000000..6eccc9e --- /dev/null +++ b/far-rcv/far-rcv.c @@ -0,0 +1,1303 @@ +/* + * Copyright (C) 2012 Alexander Block. + * Copyright (C) 2012, 2013 STRATO AG. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/* + * This is the main part of the far-rcv library. All exported functions + * are located in this file. + */ + +#define _BSD_SOURCE +#define _GNU_SOURCE +#define _ATFILE_SOURCE + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <stdint.h> +#include <inttypes.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <netinet/in.h> +#if !defined (HAVE_UTIMENSAT) +#include <utime.h> +#endif /* !defined (HAVE_UTIMENSAT) */ + +#include "far-rcv.h" +#include "far-crc32c.h" +#include "far-endian.h" +#include "far-xattr.h" + + +#if !defined (O_NOATIME) +#define O_NOATIME 0 +#endif + + +#define __TLV_GOTO_FAIL(expr) \ + if ((ret = expr) < 0) \ + goto tlv_get_failed; + +#define __TLV_DO_WHILE_GOTO_FAIL(expr) \ + do { \ + __TLV_GOTO_FAIL(expr) \ + } while (0) + + +#define TLV_GET(s, attr, data, len) \ + __TLV_DO_WHILE_GOTO_FAIL(tlv_get(s, attr, data, len)) + +#define TLV_CHECK_LEN(expected, got) \ + do { \ + if (expected != got) { \ + fprintf(stderr, \ + "ERROR: invalid size for attribute. expected = %d, got = %d\n", \ + (int)expected, (int)got); \ + ret = -EINVAL; \ + goto tlv_get_failed; \ + } \ + } while (0) + +#define TLV_GET_INT(s, attr, bits, v) \ + do { \ + uint##bits##_t __tmp; \ + void *__ptr; \ + int __len; \ + TLV_GET(s, attr, &__ptr, &__len); \ + TLV_CHECK_LEN(sizeof(__tmp), __len); \ + memcpy(&__tmp, __ptr, sizeof(__tmp)); \ + *v = far_rcv_le##bits##toh(__tmp); \ + } while (0) + +#define far_rcv_le8toh(v) (v) +#define TLV_GET_U8(s, attr, v) TLV_GET_INT(s, attr, 8, v) +#define TLV_GET_U16(s, attr, v) TLV_GET_INT(s, attr, 16, v) +#define TLV_GET_U32(s, attr, v) TLV_GET_INT(s, attr, 32, v) +#define TLV_GET_U64(s, attr, v) TLV_GET_INT(s, attr, 64, v) + +#define TLV_GET_STRING(s, attr, str) \ + __TLV_DO_WHILE_GOTO_FAIL(tlv_get_string(s, attr, str)) + +#define TLV_GET_TIMESPEC(s, attr, ts) \ + __TLV_DO_WHILE_GOTO_FAIL(tlv_get_timespec(s, attr, ts)) + +#define TLV_GET_UUID(s, attr, uuid) \ + __TLV_DO_WHILE_GOTO_FAIL(tlv_get_uuid(s, attr, uuid)) + + +#define FAR_SEND_STREAM_MAGIC "far-stream\0\0" +#define FAR_SEND_STREAM_MAGIC_ALT "btrfs-stream" +#define FAR_SEND_STREAM_VERSION 1 + +#define FAR_SEND_BUF_SIZE (1024 * 64) +#define FAR_SEND_READ_SIZE (1024 * 48) + +enum far_tlv_type { + FAR_TLV_U8, + FAR_TLV_U16, + FAR_TLV_U32, + FAR_TLV_U64, + FAR_TLV_BINARY, + FAR_TLV_STRING, + FAR_TLV_UUID, + FAR_TLV_TIMESPEC, +}; + +struct far_timespec { + uint64_t sec; + uint32_t nsec; +} __attribute__ ((__packed__)); + +struct far_stream_header { + char magic[sizeof(FAR_SEND_STREAM_MAGIC)]; + uint32_t version; +} __attribute__ ((__packed__)); + +struct far_cmd_header { + /* len excluding the header */ + uint32_t len; + uint16_t cmd; + /* crc including the header with zero crc field */ + uint32_t crc; +} __attribute__ ((__packed__)); + +struct far_tlv_header { + uint16_t tlv_type; + /* len excluding the header */ + uint16_t tlv_len; +} __attribute__ ((__packed__)); + +/* commands */ +enum far_send_cmd { + FAR_SEND_C_UNSPEC, + + FAR_SEND_C_SUBVOL, + FAR_SEND_C_SNAPSHOT, + + FAR_SEND_C_MKFILE, + FAR_SEND_C_MKDIR, + FAR_SEND_C_MKNOD, + FAR_SEND_C_MKFIFO, + FAR_SEND_C_MKSOCK, + FAR_SEND_C_SYMLINK, + + FAR_SEND_C_RENAME, + FAR_SEND_C_LINK, + FAR_SEND_C_UNLINK, + FAR_SEND_C_RMDIR, + + FAR_SEND_C_SET_XATTR, + FAR_SEND_C_REMOVE_XATTR, + + FAR_SEND_C_WRITE, + FAR_SEND_C_CLONE, + + FAR_SEND_C_TRUNCATE, + FAR_SEND_C_CHMOD, + FAR_SEND_C_CHOWN, + FAR_SEND_C_UTIMES, + + FAR_SEND_C_END, + __FAR_SEND_C_MAX, +}; +#define FAR_SEND_C_MAX (__FAR_SEND_C_MAX - 1) + +/* attributes in send stream */ +enum { + FAR_SEND_A_UNSPEC, + + FAR_SEND_A_UUID, + FAR_SEND_A_CTRANSID, + + FAR_SEND_A_INO, + FAR_SEND_A_SIZE, + FAR_SEND_A_MODE, + FAR_SEND_A_UID, + FAR_SEND_A_GID, + FAR_SEND_A_RDEV, + FAR_SEND_A_CTIME, + FAR_SEND_A_MTIME, + FAR_SEND_A_ATIME, + FAR_SEND_A_OTIME, + + FAR_SEND_A_XATTR_NAME, + FAR_SEND_A_XATTR_DATA, + + FAR_SEND_A_PATH, + FAR_SEND_A_PATH_TO, + FAR_SEND_A_PATH_LINK, + + FAR_SEND_A_FILE_OFFSET, + FAR_SEND_A_DATA, + + FAR_SEND_A_CLONE_UUID, + FAR_SEND_A_CLONE_CTRANSID, + FAR_SEND_A_CLONE_PATH, + FAR_SEND_A_CLONE_OFFSET, + FAR_SEND_A_CLONE_LEN, + + __FAR_SEND_A_MAX, +}; +#define FAR_SEND_A_MAX (__FAR_SEND_A_MAX - 1) + +struct far_send_stream { + int fd; + char read_buf[FAR_SEND_BUF_SIZE]; + + int cmd; + struct far_tlv_header *cmd_attrs[FAR_SEND_A_MAX + 1]; + uint32_t version; +}; + + +static int read_and_process_send_stream(struct far_rcv_ctx *frctx, int fd); +static int read_buf(struct far_send_stream *s, void *buf, int len); +static int read_cmd(struct far_send_stream *s); +static int tlv_get(struct far_send_stream *s, int attr, void **data, int *len); +static int tlv_get_string(struct far_send_stream *s, int attr, char **str); +static int tlv_get_timespec(struct far_send_stream *s, int attr, + struct timespec *ts); +static int tlv_get_uuid(struct far_send_stream *s, int attr, + unsigned char *uuid); +static int read_and_process_cmd(struct far_rcv_ctx *frctx, + struct far_send_stream *s); +static int process_mkfile(struct far_rcv_ctx *frctx, const char *path); +static int process_mkdir(struct far_rcv_ctx *frctx, const char *path); +static int process_mknod(struct far_rcv_ctx *frctx, const char *path, + uint64_t mode, uint64_t dev); +static int process_mkfifo(struct far_rcv_ctx *frctx, const char *path); +static int process_mksock(struct far_rcv_ctx *frctx, const char *path); +static int process_symlink(struct far_rcv_ctx *frctx, const char *path, + const char *lnk); +static int process_rename(struct far_rcv_ctx *frctx, const char *from, + const char *to); +static int process_link(struct far_rcv_ctx *frctx, const char *path, + const char *lnk); +static int process_unlink(struct far_rcv_ctx *frctx, const char *path); +static int process_rmdir(struct far_rcv_ctx *frctx, const char *path); +static int open_inode_for_write(struct far_rcv_ctx *frctx, const char *path); +static int close_inode_for_write(struct far_rcv_ctx *frctx); +static int process_write(struct far_rcv_ctx *frctx, const char *path, + const void *data, uint64_t offset, uint64_t len); +static int process_clone(struct far_rcv_ctx *frctx, const char *path, + uint64_t offset, uint64_t len, + const unsigned char *clone_uuid, + uint64_t clone_ctransid, const char *clone_path, + uint64_t clone_offset); +static int process_set_xattr(struct far_rcv_ctx *frctx, const char *path, + const char *name, const void *data, int len); +static int process_remove_xattr(struct far_rcv_ctx *frctx, const char *path, + const char *name); +static int process_truncate(struct far_rcv_ctx *frctx, const char *path, + uint64_t size); +static int process_chmod(struct far_rcv_ctx *frctx, const char *path, + uint64_t mode); +static int process_chown(struct far_rcv_ctx *frctx, const char *path, + uint64_t uid, uint64_t gid); +static int process_utimes(struct far_rcv_ctx *frctx, const char *path, + struct timespec *at, struct timespec *mt, + struct timespec *ct); + + +int far_rcv_init(struct far_rcv_ctx *frctx) +{ + assert(frctx); + + memset(frctx, 0, sizeof(*frctx)); + frctx->verbose = 0; + frctx->support_xattrs = 1; + frctx->free_current_base_path = 0; + frctx->current_base_path = NULL; + frctx->write_fd = -1; + frctx->write_path = NULL; + + frctx->ops.finish_subvol = NULL; + frctx->ops.subvol = NULL; + frctx->ops.snapshot = NULL; + frctx->ops.mkfile = process_mkfile; + frctx->ops.mkdir = process_mkdir; + frctx->ops.mknod = process_mknod; + frctx->ops.mkfifo = process_mkfifo; + frctx->ops.mksock = process_mksock; + frctx->ops.symlink = process_symlink; + frctx->ops.rename = process_rename; + frctx->ops.link = process_link; + frctx->ops.unlink = process_unlink; + frctx->ops.rmdir = process_rmdir; + frctx->ops.open_inode_for_write = open_inode_for_write; + frctx->ops.close_inode_for_write = close_inode_for_write; + frctx->ops.write = process_write; + frctx->ops.clone = process_clone; + frctx->ops.set_xattr = process_set_xattr; + frctx->ops.remove_xattr = process_remove_xattr; + frctx->ops.truncate = process_truncate; + frctx->ops.chmod = process_chmod; + frctx->ops.chown = process_chown; + frctx->ops.utimes = process_utimes; + + return 0; +} + +void far_rcv_deinit(struct far_rcv_ctx *frctx) +{ + if (frctx->free_current_base_path) { + free((void *)frctx->current_base_path); + frctx->free_current_base_path = 0; + } + frctx->current_base_path = NULL; + frctx->root_path = NULL; + free((void *)frctx->write_path); + frctx->write_path = NULL; + if (frctx->write_fd != -1) { + close(frctx->write_fd); + frctx->write_fd = -1; + } +} + +int far_rcv_mainloop(struct far_rcv_ctx *frctx, int stream_fd, + const char *root_path) +{ + int end = 0; + int ret; + frctx->root_path = root_path; + frctx->current_base_path = root_path; + frctx->free_current_base_path = 0; + + while (!end) { + ret = read_and_process_send_stream(frctx, stream_fd); + if (ret < 0) + goto out; + if (ret) + end = 1; + + ret = frctx->ops.close_inode_for_write(frctx); + if (ret < 0) + goto out; + ret = frctx->ops.finish_subvol(frctx); + if (ret < 0) + goto out; + } + ret = 0; + +out: + far_rcv_deinit(frctx); + return ret; +} + +static int read_and_process_send_stream(struct far_rcv_ctx *frctx, int fd) +{ + int ret; + struct far_send_stream s; + struct far_stream_header hdr; + + s.fd = fd; + + ret = read_buf(&s, &hdr, sizeof(hdr)); + if (ret < 0) + goto out; + if (ret) { + ret = 1; + goto out; + } + + if (strcmp(hdr.magic, FAR_SEND_STREAM_MAGIC) && + strcmp(hdr.magic, FAR_SEND_STREAM_MAGIC_ALT)) { + ret = -EINVAL; + fprintf(stderr, "ERROR: Unexpected header\n"); + goto out; + } + + s.version = far_rcv_le32toh(hdr.version); + if (s.version > FAR_SEND_STREAM_VERSION) { + ret = -EINVAL; + fprintf(stderr, + "ERROR: Stream version %d not supported. Please upgrade.\n", + s.version); + goto out; + } + + while (1) { + ret = read_and_process_cmd(frctx, &s); + if (ret < 0) + goto out; + if (ret) { + /* received end marker */ + goto out; + } + } + +out: + return ret; +} + +static int read_buf(struct far_send_stream *s, void *buf, int len) +{ + int ret; + int pos = 0; + + while (pos < len) { + ret = read(s->fd, (char*)buf + pos, len - pos); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "ERROR: read from stream failed. %s\n", + strerror(-ret)); + goto out; + } + if (ret == 0) { + ret = 1; + goto out; + } + pos += ret; + } + + ret = 0; + +out: + return ret; +} + +/* + * Reads a single command and decodes the TLV's into s->cmd_attrs + */ +static int read_cmd(struct far_send_stream *s) +{ + int ret; + int cmd; + int cmd_len; + int tlv_type; + int tlv_len; + char *data; + int pos; + struct far_tlv_header tlv_hdr; + struct far_cmd_header cmd_hdr; + uint32_t crc; + uint32_t crc2; + + memset(s->cmd_attrs, 0, sizeof(s->cmd_attrs)); + + ret = read_buf(s, &cmd_hdr, sizeof(cmd_hdr)); + if (ret < 0) + goto out; + if (ret) { + ret = -EINVAL; + fprintf(stderr, "ERROR: unexpected EOF in stream.\n"); + goto out; + } + + cmd = far_rcv_le16toh(cmd_hdr.cmd); + cmd_len = far_rcv_le32toh(cmd_hdr.len); + crc = far_rcv_le32toh(cmd_hdr.crc); + cmd_hdr.crc = 0; + crc2 = far_crc32c_calc(0, (unsigned char*)&cmd_hdr, sizeof(cmd_hdr)); + + ret = read_buf(s, s->read_buf, cmd_len); + if (ret < 0) + goto out; + if (ret) { + ret = -EINVAL; + fprintf(stderr, "ERROR: unexpected EOF in stream.\n"); + goto out; + } + + crc2 = far_crc32c_calc(crc2, (unsigned char*)s->read_buf, cmd_len); + + if (crc != crc2) { + ret = -EINVAL; + fprintf(stderr, "ERROR: crc32 mismatch in command.\n"); + goto out; + } + + pos = 0; + data = s->read_buf; + while (pos < cmd_len) { + memcpy(&tlv_hdr, data, sizeof(tlv_hdr)); + tlv_type = far_rcv_le16toh(tlv_hdr.tlv_type); + tlv_len = far_rcv_le16toh(tlv_hdr.tlv_len); + + if (tlv_type <= 0 || tlv_type > FAR_SEND_A_MAX || + tlv_len < 0 || tlv_len > FAR_SEND_BUF_SIZE) { + fprintf(stderr, + "ERROR: invalid tlv in cmd. tlv_type = %d, tlv_len = %d\n", + tlv_type, tlv_len); + ret = -EINVAL; + goto out; + } + + s->cmd_attrs[tlv_type] = (struct far_tlv_header *)data; + data += sizeof(tlv_hdr) + tlv_len; + pos += sizeof(tlv_hdr) + tlv_len; + } + + s->cmd = cmd; + ret = 0; + +out: + return ret; +} + +static int tlv_get(struct far_send_stream *s, int attr, void **data, int *len) +{ + int ret; + struct far_tlv_header tlv_hdr; + struct far_tlv_header *h; + + if (attr <= 0 || attr > FAR_SEND_A_MAX) { + fprintf(stderr, + "ERROR: invalid attribute requested. attr = %d\n", + attr); + ret = -EINVAL; + goto out; + } + + h = s->cmd_attrs[attr]; + if (!h) { + fprintf(stderr, + "ERROR: attribute %d requested but not present.\n", + attr); + ret = -ENOENT; + goto out; + } + + memcpy(&tlv_hdr, h, sizeof(tlv_hdr)); + *len = far_rcv_le16toh(tlv_hdr.tlv_len); + *data = h + 1; + + ret = 0; + +out: + return ret; +} + +static int tlv_get_string(struct far_send_stream *s, int attr, char **str) +{ + int ret; + void *data; + int len; + + TLV_GET(s, attr, &data, &len); + + *str = malloc(len + 1); + if (!*str) + return -ENOMEM; + + memcpy(*str, data, len); + (*str)[len] = 0; + ret = 0; + +tlv_get_failed: + return ret; +} + +static int tlv_get_timespec(struct far_send_stream *s, int attr, + struct timespec *ts) +{ + int ret; + int len; + struct far_timespec bts; + void *ptr; + + TLV_GET(s, attr, &ptr, &len); + TLV_CHECK_LEN(sizeof(bts), len); + + memcpy(&bts, ptr, sizeof(bts)); + ts->tv_sec = far_rcv_le64toh(bts.sec); + ts->tv_nsec = far_rcv_le32toh(bts.nsec); + ret = 0; + +tlv_get_failed: + return ret; +} + +static int tlv_get_uuid(struct far_send_stream *s, int attr, + unsigned char *uuid) +{ + int ret; + int len; + void *data; + + TLV_GET(s, attr, &data, &len); + TLV_CHECK_LEN(FAR_UUID_SIZE, len); + memcpy(uuid, data, FAR_UUID_SIZE); + + ret = 0; + +tlv_get_failed: + return ret; +} + +static int read_and_process_cmd(struct far_rcv_ctx *frctx, + struct far_send_stream *s) +{ + int ret; + char *path = NULL; + char *path_to = NULL; + char *clone_path = NULL; + char *xattr_name = NULL; + void *xattr_data = NULL; + void *data = NULL; + struct timespec at; + struct timespec ct; + struct timespec mt; + unsigned char uuid[FAR_UUID_SIZE]; + unsigned char clone_uuid[FAR_UUID_SIZE]; + uint64_t tmp; + uint64_t tmp2; + uint64_t ctransid; + uint64_t clone_ctransid; + uint64_t mode; + uint64_t dev; + uint64_t clone_offset; + uint64_t offset; + int len; + int xattr_len; + char *tmp_path; + + ret = read_cmd(s); + if (ret) + goto out; + + switch (s->cmd) { + case FAR_SEND_C_SUBVOL: + TLV_GET_STRING(s, FAR_SEND_A_PATH, &path); + TLV_GET_UUID(s, FAR_SEND_A_UUID, uuid); + TLV_GET_U64(s, FAR_SEND_A_CTRANSID, &ctransid); + tmp_path = strrchr(path, '/'); + if (tmp_path) + path = strdup(tmp_path + 1); + ret = frctx->ops.subvol(frctx, path, uuid, ctransid); + break; + case FAR_SEND_C_SNAPSHOT: + TLV_GET_STRING(s, FAR_SEND_A_PATH, &path); + TLV_GET_UUID(s, FAR_SEND_A_UUID, uuid); + TLV_GET_U64(s, FAR_SEND_A_CTRANSID, &ctransid); + TLV_GET_UUID(s, FAR_SEND_A_CLONE_UUID, clone_uuid); + TLV_GET_U64(s, FAR_SEND_A_CLONE_CTRANSID, &clone_ctransid); + tmp_path = strrchr(path, '/'); + if (tmp_path) + path = strdup(tmp_path + 1); + ret = frctx->ops.snapshot(frctx, path, uuid, ctransid, + clone_uuid, clone_ctransid); + break; + case FAR_SEND_C_MKFILE: + TLV_GET_STRING(s, FAR_SEND_A_PATH, &path); + ret = frctx->ops.mkfile(frctx, path); + break; + case FAR_SEND_C_MKDIR: + TLV_GET_STRING(s, FAR_SEND_A_PATH, &path); + ret = frctx->ops.mkdir(frctx, path); + break; + case FAR_SEND_C_MKNOD: + TLV_GET_STRING(s, FAR_SEND_A_PATH, &path); + TLV_GET_U64(s, FAR_SEND_A_MODE, &mode); + TLV_GET_U64(s, FAR_SEND_A_RDEV, &dev); + ret = frctx->ops.mknod(frctx, path, mode, dev); + break; + case FAR_SEND_C_MKFIFO: + TLV_GET_STRING(s, FAR_SEND_A_PATH, &path); + ret = frctx->ops.mkfifo(frctx, path); + break; + case FAR_SEND_C_MKSOCK: + TLV_GET_STRING(s, FAR_SEND_A_PATH, &path); + ret = frctx->ops.mksock(frctx, path); + break; + case FAR_SEND_C_SYMLINK: + TLV_GET_STRING(s, FAR_SEND_A_PATH, &path); + TLV_GET_STRING(s, FAR_SEND_A_PATH_LINK, &path_to); + ret = frctx->ops.symlink(frctx, path, path_to); + break; + case FAR_SEND_C_RENAME: + TLV_GET_STRING(s, FAR_SEND_A_PATH, &path); + TLV_GET_STRING(s, FAR_SEND_A_PATH_TO, &path_to); + ret = frctx->ops.rename(frctx, path, path_to); + break; + case FAR_SEND_C_LINK: + TLV_GET_STRING(s, FAR_SEND_A_PATH, &path); + TLV_GET_STRING(s, FAR_SEND_A_PATH_LINK, &path_to); + ret = frctx->ops.link(frctx, path, path_to); + break; + case FAR_SEND_C_UNLINK: + TLV_GET_STRING(s, FAR_SEND_A_PATH, &path); + ret = frctx->ops.unlink(frctx, path); + break; + case FAR_SEND_C_RMDIR: + TLV_GET_STRING(s, FAR_SEND_A_PATH, &path); + ret = frctx->ops.rmdir(frctx, path); + break; + case FAR_SEND_C_WRITE: + TLV_GET_STRING(s, FAR_SEND_A_PATH, &path); + TLV_GET_U64(s, FAR_SEND_A_FILE_OFFSET, &offset); + TLV_GET(s, FAR_SEND_A_DATA, &data, &len); + ret = frctx->ops.write(frctx, path, data, offset, len); + break; + case FAR_SEND_C_CLONE: + TLV_GET_STRING(s, FAR_SEND_A_PATH, &path); + TLV_GET_U64(s, FAR_SEND_A_FILE_OFFSET, &offset); + TLV_GET_U64(s, FAR_SEND_A_CLONE_LEN, &len); + TLV_GET_UUID(s, FAR_SEND_A_CLONE_UUID, clone_uuid); + TLV_GET_U64(s, FAR_SEND_A_CLONE_CTRANSID, &clone_ctransid); + TLV_GET_STRING(s, FAR_SEND_A_CLONE_PATH, &clone_path); + TLV_GET_U64(s, FAR_SEND_A_CLONE_OFFSET, &clone_offset); + ret = frctx->ops.clone(frctx, path, offset, len, clone_uuid, + clone_ctransid, clone_path, + clone_offset); + break; + case FAR_SEND_C_SET_XATTR: + TLV_GET_STRING(s, FAR_SEND_A_PATH, &path); + TLV_GET_STRING(s, FAR_SEND_A_XATTR_NAME, &xattr_name); + TLV_GET(s, FAR_SEND_A_XATTR_DATA, &xattr_data, &xattr_len); + if (!frctx->support_xattrs) + break; + ret = frctx->ops.set_xattr(frctx, path, xattr_name, xattr_data, + xattr_len); + break; + case FAR_SEND_C_REMOVE_XATTR: + TLV_GET_STRING(s, FAR_SEND_A_PATH, &path); + TLV_GET_STRING(s, FAR_SEND_A_XATTR_NAME, &xattr_name); + if (!frctx->support_xattrs) + break; + ret = frctx->ops.remove_xattr(frctx, path, xattr_name); + break; + case FAR_SEND_C_TRUNCATE: + TLV_GET_STRING(s, FAR_SEND_A_PATH, &path); + TLV_GET_U64(s, FAR_SEND_A_SIZE, &tmp); + ret = frctx->ops.truncate(frctx, path, tmp); + break; + case FAR_SEND_C_CHMOD: + TLV_GET_STRING(s, FAR_SEND_A_PATH, &path); + TLV_GET_U64(s, FAR_SEND_A_MODE, &tmp); + ret = frctx->ops.chmod(frctx, path, tmp); + break; + case FAR_SEND_C_CHOWN: + TLV_GET_STRING(s, FAR_SEND_A_PATH, &path); + TLV_GET_U64(s, FAR_SEND_A_UID, &tmp); + TLV_GET_U64(s, FAR_SEND_A_GID, &tmp2); + ret = frctx->ops.chown(frctx, path, tmp, tmp2); + break; + case FAR_SEND_C_UTIMES: + TLV_GET_STRING(s, FAR_SEND_A_PATH, &path); + TLV_GET_TIMESPEC(s, FAR_SEND_A_ATIME, &at); + TLV_GET_TIMESPEC(s, FAR_SEND_A_MTIME, &mt); + TLV_GET_TIMESPEC(s, FAR_SEND_A_CTIME, &ct); + ret = frctx->ops.utimes(frctx, path, &at, &mt, &ct); + break; + case FAR_SEND_C_END: + if (frctx->verbose >= 2) + fprintf(stderr, "receive end marker\n"); + ret = 1; + break; + } + +tlv_get_failed: +out: + free(path); + free(path_to); + free(clone_path); + free(xattr_name); + return ret; +} + +static int process_mkfile(struct far_rcv_ctx *frctx, const char *path) +{ + int ret; + char *full_path = far_rcv_path_cat(frctx->current_base_path, path); + + if (frctx->verbose >= 2) + fprintf(stderr, "mkfile %s\n", path); + + ret = creat(full_path, 0600); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "ERROR: mkfile %s failed. %s\n", path, + strerror(-ret)); + goto out; + } + close(ret); + ret = 0; + +out: + free(full_path); + return ret; +} + +static int process_mkdir(struct far_rcv_ctx *frctx, const char *path) +{ + int ret; + char *full_path = far_rcv_path_cat(frctx->current_base_path, path); + + if (frctx->verbose >= 2) + fprintf(stderr, "mkdir %s\n", path); + + ret = mkdir(full_path, 0700); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "ERROR: mkdir %s failed. %s\n", path, + strerror(-ret)); + } + + free(full_path); + return ret; +} + +static int process_mknod(struct far_rcv_ctx *frctx, const char *path, + uint64_t mode, uint64_t dev) +{ + int ret; + char *full_path = far_rcv_path_cat(frctx->current_base_path, path); + + if (frctx->verbose >= 2) + fprintf(stderr, "mknod %s mode=%" PRIu64 ", dev=%" PRIu64 "\n", + path, mode, dev); + + ret = mknod(full_path, mode & S_IFMT, dev); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "ERROR: mknod %s failed. %s\n", path, + strerror(-ret)); + } + + free(full_path); + return ret; +} + +static int process_mkfifo(struct far_rcv_ctx *frctx, const char *path) +{ + int ret; + char *full_path = far_rcv_path_cat(frctx->current_base_path, path); + + if (frctx->verbose >= 2) + fprintf(stderr, "mkfifo %s\n", path); + + ret = mkfifo(full_path, 0600); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "ERROR: mkfifo %s failed. %s\n", path, + strerror(-ret)); + } + + free(full_path); + return ret; +} + +static int process_mksock(struct far_rcv_ctx *frctx, const char *path) +{ + int ret; + char *full_path = far_rcv_path_cat(frctx->current_base_path, path); + + if (frctx->verbose >= 2) + fprintf(stderr, "mksock %s\n", path); + + ret = mknod(full_path, 0600 | S_IFSOCK, 0); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "ERROR: mknod %s failed. %s\n", path, + strerror(-ret)); + } + + free(full_path); + return ret; +} + +static int process_symlink(struct far_rcv_ctx *frctx, const char *path, + const char *lnk) +{ + int ret; + char *full_path = far_rcv_path_cat(frctx->current_base_path, path); + + if (frctx->verbose >= 2) + fprintf(stderr, "symlink %s -> %s\n", path, lnk); + + ret = symlink(lnk, full_path); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "ERROR: symlink %s -> %s failed. %s\n", path, + lnk, strerror(-ret)); + } + + free(full_path); + return ret; +} + +static int process_rename(struct far_rcv_ctx *frctx, const char *from, + const char *to) +{ + int ret; + char *full_from = far_rcv_path_cat(frctx->current_base_path, from); + char *full_to = far_rcv_path_cat(frctx->current_base_path, to); + + if (frctx->verbose >= 2) + fprintf(stderr, "rename %s -> %s\n", from, to); + + ret = rename(full_from, full_to); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "ERROR: rename %s -> %s failed. %s\n", from, + to, strerror(-ret)); + } + + free(full_from); + free(full_to); + return ret; +} + +static int process_link(struct far_rcv_ctx *frctx, const char *path, + const char *lnk) +{ + int ret; + char *full_path = far_rcv_path_cat(frctx->current_base_path, path); + char *full_link_path = far_rcv_path_cat(frctx->current_base_path, lnk); + + if (frctx->verbose >= 2) + fprintf(stderr, "link %s -> %s\n", path, lnk); + + ret = link(full_link_path, full_path); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "ERROR: link %s -> %s failed. %s\n", path, + lnk, strerror(-ret)); + } + + free(full_path); + free(full_link_path); + return ret; +} + + +static int process_unlink(struct far_rcv_ctx *frctx, const char *path) +{ + int ret; + char *full_path = far_rcv_path_cat(frctx->current_base_path, path); + + if (frctx->verbose >= 2) + fprintf(stderr, "unlink %s\n", path); + + ret = unlink(full_path); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "ERROR: unlink %s failed. %s\n", path, + strerror(-ret)); + } + + free(full_path); + return ret; +} + +static int process_rmdir(struct far_rcv_ctx *frctx, const char *path) +{ + int ret; + char *full_path = far_rcv_path_cat(frctx->current_base_path, path); + + if (frctx->verbose >= 2) + fprintf(stderr, "rmdir %s\n", path); + + ret = rmdir(full_path); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "ERROR: rmdir %s failed. %s\n", path, + strerror(-ret)); + } + + free(full_path); + return ret; +} + +static int open_inode_for_write(struct far_rcv_ctx *frctx, const char *path) +{ + int ret = 0; + + if (frctx->write_fd != -1) { + if (strcmp(frctx->write_path, path) == 0) + goto out; + close(frctx->write_fd); + frctx->write_fd = -1; + } + + frctx->write_fd = open(path, O_RDWR); + if (frctx->write_fd < 0) { + ret = -errno; + fprintf(stderr, "ERROR: open %s failed. %s\n", path, + strerror(-ret)); + goto out; + } + free((void *)frctx->write_path); + frctx->write_path = strdup(path); + +out: + return ret; +} + +static int close_inode_for_write(struct far_rcv_ctx *frctx) +{ + free((void *)frctx->write_path); + frctx->write_path = NULL; + if (frctx->write_fd != -1) { + close(frctx->write_fd); + frctx->write_fd = -1; + } + + return 0; +} + +static int process_write(struct far_rcv_ctx *frctx, const char *path, + const void *data, uint64_t offset, uint64_t len) +{ + int ret = 0; + char *full_path = far_rcv_path_cat(frctx->current_base_path, path); + uint64_t pos = 0; + int w; + + ret = frctx->ops.open_inode_for_write(frctx, full_path); + if (ret < 0) + goto out; + + while (pos < len) { + w = pwrite(frctx->write_fd, (char*)data + pos, len - pos, + offset + pos); + if (w < 0) { + ret = -errno; + fprintf(stderr, "ERROR: writing to %s failed. %s\n", + path, strerror(-ret)); + goto out; + } + pos += w; + } + +out: + free(full_path); + return ret; +} + +static int process_clone(struct far_rcv_ctx *frctx, const char *path, + uint64_t offset, uint64_t len, + const unsigned char *clone_uuid, + uint64_t clone_ctransid, const char *clone_path, + uint64_t clone_offset) +{ + int ret; + char *full_dst_path = far_rcv_path_cat(frctx->current_base_path, path); + char *full_clone_path = NULL; + int clone_fd = -1; + + ret = frctx->ops.open_inode_for_write(frctx, full_dst_path); + if (ret < 0) + goto out; + + full_clone_path = far_rcv_path_cat(frctx->current_base_path, + clone_path); + + clone_fd = open(full_clone_path, O_RDONLY | O_NOATIME); + if (clone_fd < 0) { + ret = -errno; + fprintf(stderr, "ERROR: failed to open %s. %s\n", + full_clone_path, strerror(-ret)); + goto out; + } + + if (lseek(frctx->write_fd, offset, SEEK_SET) == (off_t)-1) { + ret = -errno; + fprintf(stderr, "ERROR: failed to lseek %s. %s\n", + full_dst_path, strerror(-ret)); + goto out; + } + if (lseek(clone_fd, clone_offset, SEEK_SET) == (off_t)-1) { + ret = -errno; + fprintf(stderr, "ERROR: failed to lseek %s. %s\n", + full_clone_path, strerror(-ret)); + goto out; + } + + while (len > 0) { + char buf[4096]; + uint64_t sub_len; + int read_len; + int write_len; + int offset; + + sub_len = sizeof(buf); + if (sub_len > len) + sub_len = len; + read_len = read(clone_fd, buf, sub_len); + if (read_len < 0) { + if (errno == EINTR) + continue; + ret = -errno; + fprintf(stderr, "ERROR: failed to read from %s. %s\n", + full_clone_path, strerror(-ret)); + goto out; + } else if (read_len == 0) { + ret = -ESPIPE; /* Hmm */ + fprintf(stderr, + "ERROR: premature EOF while reading from %s.\n", + full_clone_path); + goto out; + } + + len -= read_len; + offset = 0; + do { + write_len = write(frctx->write_fd, buf + offset, + read_len); + if (write_len < 0) { + if (errno == EINTR) + continue; + ret = -errno; + fprintf(stderr, + "ERROR: failed to write to %s. %s\n", + full_dst_path, strerror(-ret)); + goto out; + } + offset += write_len; + read_len -= write_len; + } while (read_len > 0); + } + +out: + free(full_dst_path); + free(full_clone_path); + if (clone_fd != -1) + close(clone_fd); + return ret; +} + +static int process_set_xattr(struct far_rcv_ctx *frctx, const char *path, + const char *name, const void *data, int len) +{ + int ret = 0; + char *full_path = far_rcv_path_cat(frctx->current_base_path, path); + + if (frctx->verbose >= 2) + fprintf(stderr, + "set_xattr %s - name=%s data_len=%d data=%.*s\n", + path, name, len, len, (char*)data); + + ret = far_rcv_lsetxattr(full_path, name, data, len); + if (ret < 0) { + fprintf(stderr, + "ERROR: far_rcv_lsetxattr %s %s=%.*s failed. %s\n", + path, name, len, (char*)data, strerror(-ret)); + goto out; + } + +out: + free(full_path); + return ret; +} + +static int process_remove_xattr(struct far_rcv_ctx *frctx, const char *path, + const char *name) +{ + int ret = 0; + char *full_path = far_rcv_path_cat(frctx->current_base_path, path); + + if (frctx->verbose >= 2) + fprintf(stderr, "remove_xattr %s - name=%s\n", path, name); + + ret = far_rcv_lremovexattr(full_path, name); + if (ret < 0) { + fprintf(stderr, + "ERROR: far_rcv_lremovexattr %s %s failed. %s\n", + path, name, strerror(-ret)); + goto out; + } + +out: + free(full_path); + return ret; +} + +static int process_truncate(struct far_rcv_ctx *frctx, const char *path, + uint64_t size) +{ + int ret = 0; + char *full_path = far_rcv_path_cat(frctx->current_base_path, path); + + if (frctx->verbose >= 2) + fprintf(stderr, "truncate %s size=%" PRIu64 "\n", path, size); + + ret = truncate(full_path, size); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "ERROR: truncate %s failed. %s\n", path, + strerror(-ret)); + goto out; + } + +out: + free(full_path); + return ret; +} + +static int process_chmod(struct far_rcv_ctx *frctx, const char *path, + uint64_t mode) +{ + int ret = 0; + char *full_path = far_rcv_path_cat(frctx->current_base_path, path); + + if (frctx->verbose >= 2) + fprintf(stderr, "chmod %s - mode=0%o\n", path, (int)mode); + + ret = chmod(full_path, mode); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "ERROR: chmod %s failed. %s\n", + path, strerror(-ret)); + goto out; + } + +out: + free(full_path); + return ret; +} + +static int process_chown(struct far_rcv_ctx *frctx, const char *path, + uint64_t uid, uint64_t gid) +{ + int ret = 0; + char *full_path = far_rcv_path_cat(frctx->current_base_path, path); + + if (frctx->verbose >= 2) + fprintf(stderr, "chown %s - uid=%" PRIu64 ", gid=%" PRIu64 "\n", + path, uid, gid); + + ret = lchown(full_path, uid, gid); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "ERROR: chown %s failed. %s\n", path, + strerror(-ret)); + goto out; + } + +out: + free(full_path); + return ret; +} + +static int process_utimes(struct far_rcv_ctx *frctx, const char *path, + struct timespec *at, struct timespec *mt, + struct timespec *ct) +{ + int ret = 0; + char *full_path = far_rcv_path_cat(frctx->current_base_path, path); +#if defined (HAVE_UTIMENSAT) + struct timespec tv[2]; + + if (frctx->verbose >= 2) + fprintf(stderr, "utimes %s\n", path); + + tv[0] = *at; + tv[1] = *mt; + ret = utimensat(AT_FDCWD, full_path, tv, AT_SYMLINK_NOFOLLOW); +#else /* !defined (HAVE_UTIMENSAT) */ + struct utimbuf tv; + + tv.actime = at->tv_sec; + tv.modtime = mt->tv_sec; + ret = utime(path, &tv); +#endif /* !defined (HAVE_UTIMENSAT) */ + if (ret < 0) { + ret = -errno; + fprintf(stderr, "ERROR: utimes %s failed. %s\n", path, + strerror(-ret)); + goto out; + } + +out: + free(full_path); + return ret; +} + +char *far_rcv_path_cat(const char *p1, const char *p2) +{ + int p1_len = strlen(p1); + int p2_len = strlen(p2); + char *new = malloc(p1_len + p2_len + 2); + + if (p1_len && p1[p1_len - 1] == '/') + p1_len--; + if (p2_len && p2[p2_len - 1] == '/') + p2_len--; + sprintf(new, "%.*s/%.*s", p1_len, p1, p2_len, p2); + return new; +} diff --git a/far-rcv/far-rcv.h b/far-rcv/far-rcv.h new file mode 100644 index 0000000..03de4d3 --- /dev/null +++ b/far-rcv/far-rcv.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2012 Alexander Block. + * Copyright (C) 2012, 2013 STRATO AG. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/* + * This is the main part of the far-rcv library. All exported types and + * functions are located in this file. + */ + +#ifndef FAR_RCV_H_ +#define FAR_RCV_H_ + +#include <stdint.h> +#include <time.h> + +#define FAR_UUID_SIZE 16 + + +struct far_rcv_ctx; + +struct far_rcv_ops { + int (*finish_subvol)(struct far_rcv_ctx *frctx); + int (*subvol)(struct far_rcv_ctx *frctx, const char *path, + const unsigned char *uuid, uint64_t ctransid); + int (*snapshot)(struct far_rcv_ctx *frctx, const char *path, + const unsigned char *uuid, uint64_t ctransid, + const unsigned char *parent_uuid, + uint64_t parent_ctransid); + int (*mkfile)(struct far_rcv_ctx *frctx, const char *path); + int (*mkdir)(struct far_rcv_ctx *frctx, const char *path); + int (*mknod)(struct far_rcv_ctx *frctx, const char *path, uint64_t mode, + uint64_t dev); + int (*mkfifo)(struct far_rcv_ctx *frctx, const char *path); + int (*mksock)(struct far_rcv_ctx *frctx, const char *path); + int (*symlink)(struct far_rcv_ctx *frctx, const char *path, + const char *lnk); + int (*rename)(struct far_rcv_ctx *frctx, const char *from, + const char *to); + int (*link)(struct far_rcv_ctx *frctx, const char *path, + const char *lnk); + int (*unlink)(struct far_rcv_ctx *frctx, const char *path); + int (*rmdir)(struct far_rcv_ctx *frctx, const char *path); + int (*open_inode_for_write)(struct far_rcv_ctx *frctx, + const char *path); + int (*close_inode_for_write)(struct far_rcv_ctx *frctx); + int (*write)(struct far_rcv_ctx *frctx, const char *path, + const void *data, uint64_t offset, uint64_t len); + int (*clone)(struct far_rcv_ctx *frctx, const char *path, + uint64_t offset, uint64_t len, + const unsigned char *clone_uuid, uint64_t clone_ctransid, + const char *clone_path, uint64_t clone_offset); + int (*set_xattr)(struct far_rcv_ctx *frctx, const char *path, + const char *name, const void *data, int len); + int (*remove_xattr)(struct far_rcv_ctx *frctx, const char *path, + const char *name); + int (*truncate)(struct far_rcv_ctx *frctx, const char *path, + uint64_t size); + int (*chmod)(struct far_rcv_ctx *frctx, const char *path, + uint64_t mode); + int (*chown)(struct far_rcv_ctx *frctx, const char *path, uint64_t uid, + uint64_t gid); + int (*utimes)(struct far_rcv_ctx *frctx, const char *path, + struct timespec *at, struct timespec *mt, + struct timespec *ct); +}; + +struct far_rcv_ctx { + struct far_rcv_ops ops; + + int verbose; + int support_xattrs; + int free_current_base_path; + const char *current_base_path; + int write_fd; + const char *write_path; + const char *root_path; +}; + +int far_rcv_init(struct far_rcv_ctx *frctx); +void far_rcv_deinit(struct far_rcv_ctx *frctx); +int far_rcv_mainloop(struct far_rcv_ctx *frctx, int stream_fd, + const char *root_path); +char *far_rcv_path_cat(const char *p1, const char *p2); + +#endif /* !defined (FAR_RCV_H_) */ diff --git a/far-rcv/far-xattr.c b/far-rcv/far-xattr.c new file mode 100644 index 0000000..fd4271b --- /dev/null +++ b/far-rcv/far-xattr.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2012, 2013 STRATO AG. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/* + * Handling of extended attributes aka xattr or extattr is located in this + * file. It is different for Linux, SunOS and all the other operating systems. + * Refer to <http://en.wikipedia.org/wiki/Extended_file_attributes>. + * This current implementation supports Linux and SunOS. + * These functions are used internally in the far-rcv library. + */ + +#include <sys/types.h> +#include <errno.h> + +#if defined (__LINUX__) +#include <attr/xattr.h> +#else /* !defined (__LINUX__) */ +#include <stdio.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <assert.h> +#endif /* !defined (__LINUX__) */ + +#include "far-xattr.h" + + +int far_rcv_lsetxattr(const char *path, const char *name, const void *value, + size_t size) +{ +#if defined (__LINUX__) + if (lsetxattr(path, name, value, size, 0) < 0) + return -errno; + else + return 0; +#else /* !defined (__LINUX__) */ + int fd = -1; + int ret = 0; + + fd = attropen(path, name, O_TRUNC | O_WRONLY | O_CREAT); + if (fd < 0) { + ret = -errno; + fprintf(stderr, + "ERROR: attropen(%s, %s) for writing failed. %s\n", + path, name, strerror(-ret)); + goto out; + } + while (size) { + ret = write(fd, value, size); + if (ret == -1 && errno == EINTR) { + continue; + } else if (ret == -1) { + ret = -errno; + fprintf(stderr, + "ERROR: write() xattr %s in %s failed. %s\n", + name, path, strerror(-ret)); + goto out; + } + assert(ret > 0); + size -= ret; + value += ret; + ret = 0; + } +out: + if (fd != -1) + close(fd); + return ret; +#endif /* !defined (__LINUX__) */ +} + +int far_rcv_lremovexattr(const char *path, const char *name) +{ +#if defined (__LINUX__) + if (lremovexattr(path, name) < 0) + return -errno; + else + return 0; +#else /* !defined (__LINUX__) */ + int attrdirfd; + int ret = 0; + + attrdirfd = attropen(path, ".", O_RDONLY); + if (attrdirfd < 0) { + ret = -errno; + fprintf(stderr, + "ERROR: attropen(%s, \".\") failed. %s\n", path, + strerror(-ret)); + goto out; + } + + if (unlinkat(attrdirfd, name, 0) < 0) { + ret = -errno; + fprintf(stderr, + "ERROR: unlinkat(%s) failed in %s. %s\n", name, path, + strerror(-ret)); + goto out; + } + +out: + if (attrdirfd != -1) + close(attrdirfd); + return ret; +#endif /* !defined (__LINUX__) */ +} diff --git a/far-rcv/far-xattr.h b/far-rcv/far-xattr.h new file mode 100644 index 0000000..f0ddfed --- /dev/null +++ b/far-rcv/far-xattr.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2012, 2013 STRATO AG. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/* + * Handling of extended attributes aka xattr or extattr is located in this + * file. It is different for Linux, SunOS and all the other operating systems. + * Refer to <http://en.wikipedia.org/wiki/Extended_file_attributes>. + * This current implementation supports Linux and SunOS. + * These functions are used internally in the far-rcv library. + */ + +#if !defined (__FAR_RCV_XATTR_H) +#define __FAR_RCV_XATTR_H + +int far_rcv_lsetxattr(const char *path, const char *name, const void *value, + size_t size); +int far_rcv_lremovexattr(const char *path, const char *name); + +#endif /* !defined (__FAR_RCV_XATTR_H) */ |