File: ad_write.c

package info (click to toggle)
netatalk 3.1.12~ds-3
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 14,756 kB
  • sloc: ansic: 104,976; sh: 8,247; xml: 7,394; perl: 1,936; makefile: 1,430; python: 1,342; yacc: 309; lex: 49
file content (292 lines) | stat: -rw-r--r-- 7,104 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
/*
 * Copyright (c) 1990,1995 Regents of The University of Michigan.
 * All Rights Reserved.  See COPYRIGHT.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */

#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <errno.h>

#include <atalk/adouble.h>
#include <atalk/ea.h>
#include <atalk/bstrlib.h>
#include <atalk/bstradd.h>
#include <atalk/logger.h>
#include <atalk/util.h>
#include <atalk/errchk.h>

/* XXX: locking has to be checked before each stream of consecutive
 *      ad_writes to prevent a lock in the middle from causing problems. 
 */

ssize_t adf_pwrite(struct ad_fd *ad_fd, const void *buf, size_t count, off_t offset)
{
    ssize_t		cc;

#ifndef  HAVE_PWRITE
    if ( ad_fd->adf_off != offset ) {
	if ( lseek( ad_fd->adf_fd, offset, SEEK_SET ) < 0 ) {
	    return -1;
	}
	ad_fd->adf_off = offset;
    }
    cc = write( ad_fd->adf_fd, buf, count );
    if ( cc < 0 ) {
        return -1;
    }
    ad_fd->adf_off += cc;
#else
   cc = pwrite(ad_fd->adf_fd, buf, count, offset );
#endif
    return cc;
}

/* end is always 0 */
ssize_t ad_write(struct adouble *ad, uint32_t eid, off_t off, int end, const char *buf, size_t buflen)
{
    EC_INIT;
    struct stat		st;
    ssize_t		cc;
    off_t    r_off;

    if (ad_data_fileno(ad) == AD_SYMLINK) {
        errno = EACCES;
        return -1;
    }

    LOG(log_debug, logtype_ad, "ad_write: off: %ju, size: %zu, eabuflen: %zu",
        (uintmax_t)off, buflen, ad->ad_rlen);
    
    if ( eid == ADEID_DFORK ) {
        if ( end ) {
            if ( fstat( ad_data_fileno(ad), &st ) < 0 ) {
                return( -1 );
            }
            off = st.st_size - off;
        }
        cc = adf_pwrite(&ad->ad_data_fork, buf, buflen, off);
    } else if ( eid == ADEID_RFORK ) {
        if (end) {
            if (fstat( ad_reso_fileno(ad), &st ) < 0)
                return(-1);
            off = st.st_size - off - ad_getentryoff(ad, eid);
        }
        if (ad->ad_vers == AD_VERSION_EA) {
#ifdef HAVE_EAFD
            r_off = off;
#else
            r_off = ADEDOFF_RFORK_OSX + off;
#endif
        } else {
            r_off = ad_getentryoff(ad, eid) + off;
        }
        cc = adf_pwrite(&ad->ad_resource_fork, buf, buflen, r_off);

        if ( ad->ad_rlen < off + cc )
            ad->ad_rlen = off + cc;
    } else {
        return -1; /* we don't know how to write if it's not a ressource or data fork */
    }

    if (ret != 0)
        return ret;
    return( cc );
}

/* 
 * the caller set the locks
 * ftruncate is undefined when the file length is smaller than 'size'
 */
int sys_ftruncate(int fd, off_t length)
{

#ifndef  HAVE_PWRITE
off_t           curpos;
#endif
int             err;
struct stat	st;
char            c = 0;

    if (!ftruncate(fd, length)) {
        return 0;
    }
    /* maybe ftruncate doesn't work if we try to extend the size */
    err = errno;

#ifndef  HAVE_PWRITE
    /* we only care about file pointer if we don't use pwrite */
    if ((off_t)-1 == (curpos = lseek(fd, 0, SEEK_CUR)) ) {
        errno = err;
        return -1;
    }
#endif

    if ( fstat( fd, &st ) < 0 ) {
        errno = err;
        return -1;
    }
    
    if (st.st_size > length) {
        errno = err;
        return -1;
    }

    if (lseek(fd, length -1, SEEK_SET) != length -1) {
        errno = err;
        return -1;
    }

    if (1 != write( fd, &c, 1 )) {
        /* return the write errno */
        return -1;
    }

#ifndef  HAVE_PWRITE
    if (curpos != lseek(fd, curpos,  SEEK_SET)) {
        errno = err;
        return -1;
    }
#endif

    return 0;    
}

/* ------------------------ */
int ad_rtruncate(struct adouble *ad, const char *uname, const off_t size)
{
    EC_INIT;

    /*
     * We can't delete 0 byte size resource forks either, because a
     * fork may reference the adouble handle with an open fd for the
     * file, which means we would only delete the directory entry, not
     * the file. Subsequently all code that works with fork handles
     * finds the fork open, so e.g. flushing a fork (ad_flush()) will
     * recreate ._ files.  The correct place to delete 0 byte sized
     * resource forks is in of_closefork().
     */

    EC_NEG1( sys_ftruncate(ad_reso_fileno(ad), size + ad->ad_eid[ ADEID_RFORK ].ade_off) );

    ad->ad_rlen = size;

EC_CLEANUP:
    if (ret != 0)
        LOG(log_error, logtype_ad, "ad_rtruncate(\"%s\"): %s",
            fullpathname(uname), strerror(errno));
    EC_EXIT;
}

int ad_dtruncate(struct adouble *ad, const off_t size)
{
    if (sys_ftruncate(ad_data_fileno(ad), size) < 0) {
        LOG(log_error, logtype_ad, "sys_ftruncate(fd: %d): %s",
            ad_data_fileno(ad), strerror(errno));
        return -1;
    }

    return 0;
}

/* ----------------------- */
static int copy_all(const int dfd, const void *buf,
                               size_t buflen)
{
    ssize_t cc;

    while (buflen > 0) {
        if ((cc = write(dfd, buf, buflen)) < 0) {
            switch (errno) {
            case EINTR:
                continue;
            default:
                return -1;
            }
        }
        buflen -= cc;
    }

    return 0;
}

/* -------------------------- 
 * copy only the fork data stream
*/
int copy_fork(int eid, struct adouble *add, struct adouble *ads, char *buf, size_t buflen)
{
    ssize_t cc;
    int     err = 0;
    char    fixedbuf[8192];
    char    *filebuf;
    int     sfd, dfd;

    if (buf != NULL && buflen > sizeof(fixedbuf)) {
        filebuf = buf;
    } else {
        filebuf = fixedbuf;
        buflen = sizeof(fixedbuf);
    }

    if (eid == ADEID_DFORK) {
        sfd = ad_data_fileno(ads);
        dfd = ad_data_fileno(add);
    }
    else {
        sfd = ad_reso_fileno(ads);
        dfd = ad_reso_fileno(add);
    }        

    if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
    	return -1;

    if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
    	return -1;
    	
#if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
    /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
    off_t   offset = 0;
    size_t  size;
    struct stat         st;
    #define BUF 128*1024*1024

    if (fstat(sfd, &st) == 0) {
        
        while (1) {
            if ( offset >= st.st_size) {
               return 0;
            }
            size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
            if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
                switch (errno) {
                case ENOSYS:
                case EINVAL:  /* there's no guarantee that all fs support sendfile */
                    goto no_sendfile;
                default:
                    return -1;
                }
            }
        }
    }
    no_sendfile:
    lseek(sfd, offset, SEEK_SET);
#endif 

    while (1) {
        if ((cc = read(sfd, filebuf, buflen)) < 0) {
            if (errno == EINTR)
                continue;
            err = -1;
            break;
        }

        if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
            break;
        }
    }
    return err;
}