HEIMDAL: move code from source4/heimdal* to third_party/heimdal*
[samba.git] / third_party / heimdal / lib / krb5 / store_stdio.c
1 /*
2  * Copyright (c) 2017 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #include "krb5_locl.h"
35 #include "store-int.h"
36
37 #ifndef HAVE_FSEEKO
38 #define fseeko fseek
39 #define ftello ftell
40 #endif
41
42 typedef struct stdio_storage {
43     FILE *f;
44     off_t pos;
45 } stdio_storage;
46
47 #define F(S) (((stdio_storage*)(S)->data)->f)
48 #define POS(S) (((stdio_storage*)(S)->data)->pos)
49
50 static ssize_t
51 stdio_fetch(krb5_storage * sp, void *data, size_t size)
52 {
53     char *cbuf = (char *)data;
54     ssize_t count;
55     size_t rem = size;
56
57     /* similar pattern to net_read() to support pipes */
58     while (rem > 0) {
59         count = fread(cbuf, 1, rem, F(sp));
60         if (count < 0) {
61             POS(sp) = -1;
62             if (errno == EINTR)
63                 continue;
64             else
65                 return count;
66         } else if (count == 0) {
67             if (POS(sp) >= 0)
68                 POS(sp) += size - rem;
69             return size - rem;
70         }
71         cbuf += count;
72         rem -= count;
73     }
74     if (POS(sp) >= 0)
75         POS(sp) += size;
76     return size;
77 }
78
79 static ssize_t
80 stdio_store(krb5_storage * sp, const void *data, size_t size)
81 {
82     const char *cbuf = (const char *)data;
83     ssize_t count;
84     size_t rem = size;
85
86     /* similar pattern to net_write() to support pipes */
87     while (rem > 0) {
88         count = fwrite(cbuf, 1, rem, F(sp));
89         if (count < 0) {
90             if (errno == EINTR)
91                 continue;
92             /*
93              * What does it mean to have a short write when using stdio?
94              *
95              * It can't mean much.  After all stdio is buffering, so
96              * earlier writes that appeared complete may have failed,
97              * and so we don't know how much we really failed to write.
98              */
99             POS(sp) = -1;
100             return -1;
101         }
102         if (count == 0) {
103             POS(sp) = -1;
104             return -1;
105         }
106         cbuf += count;
107         rem -= count;
108     }
109     if (POS(sp) >= 0)
110         POS(sp) += size;
111     return size;
112 }
113
114 static off_t
115 stdio_seek(krb5_storage * sp, off_t offset, int whence)
116 {
117     int save_errno = errno;
118
119     if (whence == SEEK_SET && POS(sp) == offset)
120         return POS(sp);
121
122     if (whence == SEEK_CUR && POS(sp) >= 0 && offset == 0)
123         return POS(sp);
124
125     if (fseeko(F(sp), offset, whence) != 0)
126         return -1;
127     errno = save_errno;
128     return POS(sp) = ftello(F(sp));
129 }
130
131 static int
132 stdio_trunc(krb5_storage * sp, off_t offset)
133 {
134     off_t tmpoff;
135     int save_errno = errno;
136
137     if (fflush(F(sp)) == EOF)
138         return errno;
139     tmpoff = ftello(F(sp));
140     if (tmpoff > offset)
141         tmpoff = offset;
142     if (ftruncate(fileno(F(sp)), offset) == -1)
143         return errno;
144     if (fseeko(F(sp), 0, SEEK_END) == -1)
145         return errno;
146     if (fseeko(F(sp), tmpoff, SEEK_SET) == -1)
147         return errno;
148     errno = save_errno;
149     POS(sp) = tmpoff;
150     return 0;
151 }
152
153 static int
154 stdio_sync(krb5_storage * sp)
155 {
156     if (fflush(F(sp)) == EOF)
157         return errno;
158     if (fsync(fileno(F(sp))) == -1)
159         return errno;
160     return 0;
161 }
162
163 static void
164 stdio_free(krb5_storage * sp)
165 {
166     int save_errno = errno;
167
168     if (F(sp) != NULL && fclose(F(sp)) == 0)
169         errno = save_errno;
170     F(sp) = NULL;
171 }
172
173 /**
174  * Open a krb5_storage using stdio for buffering.
175  *
176  * @return A krb5_storage on success, or NULL on out of memory error.
177  *
178  * @ingroup krb5_storage
179  *
180  * @sa krb5_storage_emem()
181  * @sa krb5_storage_from_fd()
182  * @sa krb5_storage_from_mem()
183  * @sa krb5_storage_from_readonly_mem()
184  * @sa krb5_storage_from_data()
185  * @sa krb5_storage_from_socket()
186  */
187
188 KRB5_LIB_FUNCTION krb5_storage * KRB5_LIB_CALL
189 krb5_storage_stdio_from_fd(int fd_in, const char *mode)
190 {
191     krb5_storage *sp;
192     off_t off;
193     FILE *f;
194     int saved_errno = errno;
195     int fd;
196
197     off = lseek(fd_in, 0, SEEK_CUR);
198     if (off == -1)
199         return NULL;
200
201 #ifdef _MSC_VER
202     /*
203      * This function used to try to pass the input to
204      * _get_osfhandle() to test if the value is a HANDLE
205      * but this doesn't work because doing so throws an
206      * exception that will result in Watson being triggered
207      * to file a Windows Error Report.
208      */
209     fd = _dup(fd_in);
210 #else
211     fd = dup(fd_in);
212 #endif
213
214     if (fd < 0)
215         return NULL;
216
217     f = fdopen(fd, mode);
218     if (f == NULL) {
219         (void) close(fd);
220         return NULL;
221     }
222
223     errno = saved_errno;
224
225     if (fseeko(f, off, SEEK_SET) == -1) {
226         saved_errno = errno;
227         (void) fclose(f);
228         errno = saved_errno;
229         return NULL;
230     }
231
232     errno = ENOMEM;
233     sp = malloc(sizeof(krb5_storage));
234     if (sp == NULL) {
235         saved_errno = errno;
236         (void) fclose(f);
237         errno = saved_errno;
238         return NULL;
239     }
240
241     errno = ENOMEM;
242     sp->data = malloc(sizeof(stdio_storage));
243     if (sp->data == NULL) {
244         saved_errno = errno;
245         (void) fclose(f);
246         free(sp);
247         errno = saved_errno;
248         return NULL;
249     }
250     sp->flags = 0;
251     sp->eof_code = HEIM_ERR_EOF;
252     F(sp) = f;
253     POS(sp) = off;
254     sp->fetch = stdio_fetch;
255     sp->store = stdio_store;
256     sp->seek = stdio_seek;
257     sp->trunc = stdio_trunc;
258     sp->fsync = stdio_sync;
259     sp->free = stdio_free;
260     sp->max_alloc = UINT_MAX/8;
261     return sp;
262 }