Oops, my fix to subdir.mk didn't work with existing symlinks. Fixed.
[wvapps.git] / wvsync / wvsyncfile.cc
blob894a5c07ef3aefc8168d749a9cc7d48c1e0b77aa
1 /*
2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2004 Net Integration Technologies, Inc.
5 * See wvsyncfile.h for details.
7 */
9 #include <fcntl.h>
10 #include <errno.h>
11 #include <pwd.h>
12 #include <grp.h>
13 #include <utime.h>
15 #include <wvstringlist.h>
16 #include <wvfileutils.h>
18 #include "wvsyncfile.h"
20 WvSyncFile::WvSyncFile(WvStringParm name, WvStringParm _rootdir)
21 : WvSyncObj(name), rootdir(_rootdir)
23 fd = -1;
24 myofs = 0;
25 ok = true;
26 setlastmodtime();
27 setmeta();
31 bool WvSyncFile::getdata(WvBuf &out, off_t ofs, size_t _size)
33 if (isdir())
35 // it's a directory. no contents, so we should obviously return
36 // true... but we also have to check if it exists.
37 struct stat st;
38 if (lstat(realname(), &st) != 0)
39 ok = false;
40 return true;
43 // if the file is actually a symlink we need to treat that separately.
44 if (S_ISLNK(mode))
46 assert(!ofs);
47 char *buf = (char *) out.alloc(size);
48 if (readlink(realname(), buf, size) < 0)
50 out.unalloc(size);
51 err("%s: %s\n", name, strerror(errno));
52 ok = false;
53 return true;
55 return true;
58 // do we need to open the file?
59 if (fd < 0)
61 fd = open(realname(), O_RDONLY);
62 if (fd < 0)
64 err("%s: %s\n", name, strerror(errno));
65 ok = false;
66 return true;
68 myofs = 0;
71 // do we need to seek?
72 if (ofs != myofs)
74 lseek(fd, ofs, SEEK_SET);
75 myofs = ofs;
78 // grab the data.
79 char *b = (char *) out.alloc(_size);
80 assert(b); // come on, now.
81 size_t bytes = read(fd, b, _size);
82 myofs += bytes;
84 // too much data?
85 if (bytes < _size)
86 out.unalloc(_size-bytes);
88 // ONLY done if end of file!
89 return (bytes == 0);
93 off_t WvSyncFile::approxsize() const
95 return size;
99 time_t WvSyncFile::findlastmodtime() const
101 struct stat st;
103 if (lstat(realname(), &st) < 0)
104 return -1;
106 return st.st_mtime;
110 WvString WvSyncFile::findmeta()
112 struct stat st;
113 mode = size = -1;
114 if (lstat(realname(), &st) == 0)
116 mode = st.st_mode;
117 size = st.st_size;
118 uid = st.st_uid;
119 gid = st.st_gid;
121 else
123 ok = false;
124 return "";
127 WvString uidname(uid), gidname(gid);
129 struct passwd *pw = getpwuid(uid);
130 if (pw)
132 uidname = pw->pw_name;
133 uidname.unique();
135 struct group *gr = getgrgid(gid);
136 if (gr)
138 gidname = gr->gr_name;
139 gidname.unique();
142 return WvString("%s:%s:%s:%s:%s", mode, uidname, uid, gidname, gid);
146 void WvSyncFile::applymeta(WvStringParm newmeta, time_t newmtime)
148 // parse the new meta information
149 WvStringList l;
150 l.split(newmeta, ":");
151 WvStringList::Iter i(l);
152 i.rewind();
153 if (i.next())
155 // we have a mode
156 mode_t newmode = i->num();
157 if (!S_ISLNK(mode) && !S_ISLNK(newmode))
159 chmod(realname(), newmode);
160 mode = newmode;
163 else
164 goto applymeta_error;
166 if (i.next())
168 // we have a uidname
169 WvString uidname = *i;
171 if (i.next()) // we have a uid
172 uid = i->num();
174 // if we can get a uid from the uidname given, that's better than
175 // just using the number.
176 struct passwd *pw = getpwnam(uidname);
177 if (pw)
178 uid = pw->pw_uid;
180 if (uid >= 0)
181 lchown(realname(), uid, gid);
183 else
184 goto applymeta_error;
186 if (i.next())
188 // we have a gidname
189 WvString gidname = *i;
191 if (i.next()) // we have a gid
192 gid = i->num();
194 // if we can get a gid from the gidname given, that's better than
195 // just using the number.
196 struct group *gr = getgrnam(gidname);
197 if (gr)
198 gid = gr->gr_gid;
200 if (gid >= 0)
201 lchown(realname(), uid, gid);
203 setmeta();
205 else
206 goto applymeta_error;
208 // apply the new mtime
209 if (S_ISREG(mode))
211 struct utimbuf utim;
212 utim.actime = newmtime;
213 utim.modtime = newmtime;
214 utime(realname(), &utim);
215 setlastmodtime();
218 return;
219 applymeta_error:
220 err("Incomplete metadata received for %s\n", name);
224 bool WvSyncFile::isunchanged(time_t sometime, WvStringParm somemeta) const
226 if (S_ISLNK(mode) || S_ISDIR(mode))
227 return somemeta == getmeta();
228 else
229 return somemeta == getmeta() && sometime == getlastmodtime();
233 void WvSyncFile::makecopy(WvStringParm newname) const
235 fcopy(realname(), WvString("%s/%s", rootdir, newname));
239 bool WvSyncFile::isdir() const
241 return S_ISDIR(mode);
245 bool WvSyncFile::isdir(WvStringParm somemeta) const
247 WvStringList l;
248 l.split(somemeta, ":");
249 WvStringList::Iter i(l);
250 i.rewind();
251 if (i.next())
253 mode_t somemode = i->num();
254 return S_ISDIR(somemode);
257 return WvSyncObj::isdir(somemeta);
261 bool WvSyncFile::installnew(WvStringParm fname, WvStringParm newmeta,
262 time_t newmtime)
264 // make sure it's safe
265 if (hasbeenmodified())
266 return false;
268 // out with the old...
269 ::unlink(realname());
271 // ...and in with the new.
272 // we need the new mode to do that. It's the thing before the first :
273 mode_t newmode = atoi(newmeta);
275 if (S_ISDIR(newmode))
277 mkdir(realname(), 0700);
279 else if (S_ISLNK(newmode))
281 WvFile f(fname, O_RDONLY);
282 WvDynBuf buf;
283 while (f.isok())
285 if (f.select(-1, true, false))
286 f.read(buf, 1024);
289 if (buf.used() == 0)
290 return false;
292 if (symlink((const char *) buf.get(buf.used()), realname()))
293 return false;
295 else
297 log(WvLog::Debug4, "Copying from %s to %s\n", fname, name);
298 fcopy(fname, realname());
301 applymeta(newmeta, newmtime);
303 return true;